mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Compare commits
74
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
36cc3c1db0 | ||
|
|
0893493f62 | ||
|
|
4adb758593 | ||
|
|
85550834d4 | ||
|
|
7c62778232 | ||
|
|
56ced98b27 | ||
|
|
790b325368 | ||
|
|
0f539d9748 | ||
|
|
3a89a6d28a | ||
|
|
119b7eabd4 | ||
|
|
f74131ef61 | ||
|
|
faf648e7da | ||
|
|
9687e6e6a5 | ||
|
|
09769145bf | ||
|
|
ec86cb56b5 | ||
|
|
44585156ef | ||
|
|
2707adade7 | ||
|
|
78fbf5610f | ||
|
|
56f0ec9fa3 | ||
|
|
4fb7a427b1 | ||
|
|
832b715657 | ||
|
|
34e811fd36 | ||
|
|
d3b5c6b18c | ||
|
|
39598741e4 | ||
|
|
d1009845c9 | ||
|
|
7eec3f3967 | ||
|
|
a257db3aea | ||
|
|
a746cedf59 | ||
|
|
7a261dd598 | ||
|
|
a58df61837 | ||
|
|
947e811d7f | ||
|
|
caf61440f6 | ||
|
|
9aae01787a | ||
|
|
07f8bec326 | ||
|
|
16d87c3614 | ||
|
|
a93824f5c4 | ||
|
|
095df1e8f0 | ||
|
|
aaa91954c5 | ||
|
|
168186a5ed | ||
|
|
fb38ae8553 | ||
|
|
105dc82c39 | ||
|
|
c07e6afe21 | ||
|
|
4ea6411001 | ||
|
|
68f79d8bea | ||
|
|
fb1f4e2799 | ||
|
|
e17b4b6441 | ||
|
|
2b869c2396 | ||
|
|
7c3d4fcf30 | ||
|
|
3d40f309ed | ||
|
|
73547ce28c | ||
|
|
91e63df616 | ||
|
|
4cbde55243 | ||
|
|
864b1f7a91 | ||
|
|
5131f3a129 | ||
|
|
2ee34beed5 | ||
|
|
d55b15903d | ||
|
|
5a8c8fe634 | ||
|
|
b2c38ac98c | ||
|
|
1aa00e6428 | ||
|
|
f0d2dd6774 | ||
|
|
bf007f854c | ||
|
|
55a4aa2b53 | ||
|
|
95fe891369 | ||
|
|
4d1a132737 | ||
|
|
943e37836f | ||
|
|
65e1c12dfa | ||
|
|
279d91f58e | ||
|
|
51e7a2134a | ||
|
|
90226526fa | ||
|
|
7405759593 | ||
|
|
698aba5f97 | ||
|
|
7e23140ca9 | ||
|
|
904e17473f | ||
|
|
2c5cf6c67b |
@@ -7,7 +7,7 @@ name: dotnet-format
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ["main", "feature*"]
|
branches: ["main"]
|
||||||
paths:
|
paths:
|
||||||
- dotnet/**
|
- dotnet/**
|
||||||
- '.github/workflows/dotnet-format.yml'
|
- '.github/workflows/dotnet-format.yml'
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
kind: Prompt
|
kind: Prompt
|
||||||
name: Assistant
|
name: Assistant
|
||||||
description: Helpful 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:
|
model:
|
||||||
id: =Env.AZURE_OPENAI_DEPLOYMENT_NAME
|
id: gpt-4o-mini
|
||||||
provider: AzureOpenAI
|
provider: AzureOpenAI
|
||||||
apiType: Assistants
|
apiType: Assistants
|
||||||
options:
|
options:
|
||||||
@@ -12,14 +12,14 @@ model:
|
|||||||
outputSchema:
|
outputSchema:
|
||||||
properties:
|
properties:
|
||||||
language:
|
language:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The language of the answer.
|
description: The language of the answer.
|
||||||
answer:
|
answer:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The answer text.
|
description: The answer text.
|
||||||
type:
|
type:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The type of the response.
|
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
|
kind: Prompt
|
||||||
name: Assistant
|
name: Assistant
|
||||||
description: Helpful 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:
|
model:
|
||||||
id: =Env.AZURE_OPENAI_DEPLOYMENT_NAME
|
id: gpt-4o-mini
|
||||||
provider: AzureOpenAI
|
provider: AzureOpenAI
|
||||||
apiType: Responses
|
apiType: Responses
|
||||||
options:
|
options:
|
||||||
text:
|
temperature: 0.9
|
||||||
verbosity: medium
|
topP: 0.95
|
||||||
connection:
|
|
||||||
kind: remote
|
|
||||||
endpoint: =Env.AZURE_OPENAI_ENDPOINT
|
|
||||||
outputSchema:
|
outputSchema:
|
||||||
properties:
|
properties:
|
||||||
language:
|
language:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The language of the answer.
|
description: The language of the answer.
|
||||||
answer:
|
answer:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The answer text.
|
description: The answer text.
|
||||||
type:
|
type:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The type of the response.
|
description: The type of the response.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
kind: Prompt
|
kind: Prompt
|
||||||
name: Assistant
|
name: Assistant
|
||||||
description: Helpful 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:
|
model:
|
||||||
options:
|
options:
|
||||||
temperature: 0.9
|
temperature: 0.9
|
||||||
@@ -9,10 +9,10 @@ model:
|
|||||||
outputSchema:
|
outputSchema:
|
||||||
properties:
|
properties:
|
||||||
language:
|
language:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The language of the answer.
|
description: The language of the answer.
|
||||||
answer:
|
answer:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The answer text.
|
description: The answer text.
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ description: Helpful assistant
|
|||||||
instructions: You are a helpful assistant. You answer questions using the tools provided.
|
instructions: You are a helpful assistant. You answer questions using the tools provided.
|
||||||
model:
|
model:
|
||||||
options:
|
options:
|
||||||
|
temperature: 0.9
|
||||||
|
topP: 0.95
|
||||||
allowMultipleToolCalls: true
|
allowMultipleToolCalls: true
|
||||||
chatToolMode: auto
|
chatToolMode: auto
|
||||||
tools:
|
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
|
kind: Prompt
|
||||||
name: Assistant
|
name: Assistant
|
||||||
description: Helpful 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:
|
model:
|
||||||
id: =Env.OPENAI_MODEL
|
id: gpt-4.1-mini
|
||||||
provider: OpenAI
|
provider: OpenAI
|
||||||
apiType: Assistants
|
apiType: Assistants
|
||||||
options:
|
options:
|
||||||
temperature: 0.9
|
temperature: 0.9
|
||||||
topP: 0.95
|
topP: 0.95
|
||||||
connection:
|
connection:
|
||||||
kind: key
|
kind: ApiKey
|
||||||
key: =Env.OPENAI_APIKEY
|
key: =Env.OPENAI_APIKEY
|
||||||
outputSchema:
|
outputSchema:
|
||||||
name: AssistantResponse
|
|
||||||
description: The response from the assistant.
|
|
||||||
properties:
|
properties:
|
||||||
language:
|
language:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The language of the answer.
|
description: The language of the answer.
|
||||||
answer:
|
answer:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The answer text.
|
description: The answer text.
|
||||||
type:
|
type:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The type of the response.
|
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
|
kind: Prompt
|
||||||
name: Assistant
|
name: Assistant
|
||||||
description: Helpful 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:
|
model:
|
||||||
id: =Env.OPENAI_MODEL
|
id: gpt-4.1-mini
|
||||||
provider: OpenAI
|
provider: OpenAI
|
||||||
apiType: Responses
|
apiType: Responses
|
||||||
options:
|
options:
|
||||||
text:
|
temperature: 0.9
|
||||||
verbosity: medium
|
topP: 0.95
|
||||||
connection:
|
connection:
|
||||||
kind: key
|
kind: ApiKey
|
||||||
key: =Env.OPENAI_APIKEY
|
key: =Env.OPENAI_APIKEY
|
||||||
outputSchema:
|
outputSchema:
|
||||||
properties:
|
properties:
|
||||||
language:
|
language:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The language of the answer.
|
description: The language of the answer.
|
||||||
answer:
|
answer:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The answer text.
|
description: The answer text.
|
||||||
type:
|
type:
|
||||||
kind: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
description: The type of the response.
|
description: The type of the response.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<NoWarn>$(NoWarn);NU5128</NoWarn>
|
<NoWarn>$(NoWarn);NU5128;NU1900;NU1603</NoWarn>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<TargetFrameworksCore>net10.0;net9.0;net8.0</TargetFrameworksCore>
|
<TargetFrameworksCore>net10.0;net9.0;net8.0</TargetFrameworksCore>
|
||||||
<TargetFrameworks>$(TargetFrameworksCore);netstandard2.0;net472</TargetFrameworks>
|
<TargetFrameworks>$(TargetFrameworksCore);netstandard2.0;net472</TargetFrameworks>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
<PackageVersion Include="Aspire.Microsoft.Azure.Cosmos" Version="$(AspireAppHostSdkVersion)" />
|
<PackageVersion Include="Aspire.Microsoft.Azure.Cosmos" Version="$(AspireAppHostSdkVersion)" />
|
||||||
<PackageVersion Include="CommunityToolkit.Aspire.OllamaSharp" Version="13.0.0-beta.440" />
|
<PackageVersion Include="CommunityToolkit.Aspire.OllamaSharp" Version="13.0.0-beta.440" />
|
||||||
<!-- Azure.* -->
|
<!-- Azure.* -->
|
||||||
|
<PackageVersion Include="Azure.AI.Agents" Version="2.0.0-alpha.20251107.3" />
|
||||||
<PackageVersion Include="Azure.AI.Projects" Version="1.2.0-beta.3" />
|
<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.Projects.OpenAI" Version="1.0.0-beta.4" />
|
||||||
<PackageVersion Include="Azure.AI.Agents.Persistent" Version="1.2.0-beta.7" />
|
<PackageVersion Include="Azure.AI.Agents.Persistent" Version="1.2.0-beta.7" />
|
||||||
@@ -103,6 +104,8 @@
|
|||||||
<PackageVersion Include="Microsoft.Bot.ObjectModel.Json" Version="1.2025.1106.1" />
|
<PackageVersion Include="Microsoft.Bot.ObjectModel.Json" Version="1.2025.1106.1" />
|
||||||
<PackageVersion Include="Microsoft.Bot.ObjectModel.PowerFx" Version="1.2025.1106.1" />
|
<PackageVersion Include="Microsoft.Bot.ObjectModel.PowerFx" Version="1.2025.1106.1" />
|
||||||
<PackageVersion Include="Microsoft.PowerFx.Interpreter" Version="1.5.0-build.20251008-1002" />
|
<PackageVersion Include="Microsoft.PowerFx.Interpreter" Version="1.5.0-build.20251008-1002" />
|
||||||
|
<!-- Community -->
|
||||||
|
<PackageVersion Include="System.Linq.Async" Version="6.0.3" />
|
||||||
<!-- Durable Task -->
|
<!-- Durable Task -->
|
||||||
<PackageVersion Include="Microsoft.DurableTask.Client" Version="1.16.2" />
|
<PackageVersion Include="Microsoft.DurableTask.Client" Version="1.16.2" />
|
||||||
<PackageVersion Include="Microsoft.DurableTask.Client.AzureManaged" Version="1.16.2-preview.1" />
|
<PackageVersion Include="Microsoft.DurableTask.Client.AzureManaged" Version="1.16.2-preview.1" />
|
||||||
|
|||||||
@@ -76,6 +76,7 @@
|
|||||||
<Project Path="samples/GettingStarted/Agents/Agent_Step16_ChatReduction/Agent_Step16_ChatReduction.csproj" />
|
<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_Step17_BackgroundResponses/Agent_Step17_BackgroundResponses.csproj" />
|
||||||
<Project Path="samples/GettingStarted/Agents/Agent_Step18_DeepResearch/Agent_Step18_DeepResearch.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>
|
||||||
<Folder Name="/Samples/GettingStarted/DevUI/">
|
<Folder Name="/Samples/GettingStarted/DevUI/">
|
||||||
<File Path="samples/GettingStarted/DevUI/README.md" />
|
<File Path="samples/GettingStarted/DevUI/README.md" />
|
||||||
@@ -92,6 +93,13 @@
|
|||||||
<Project Path="samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Agent_OpenAI_Step01_Running.csproj" />
|
<Project Path="samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step01_Running/Agent_OpenAI_Step01_Running.csproj" />
|
||||||
<Project Path="samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Agent_OpenAI_Step02_Reasoning.csproj" />
|
<Project Path="samples/GettingStarted/AgentWithOpenAI/Agent_OpenAI_Step02_Reasoning/Agent_OpenAI_Step02_Reasoning.csproj" />
|
||||||
</Folder>
|
</Folder>
|
||||||
|
<Folder Name="/Samples/GettingStarted/DeclarativeAgents/">
|
||||||
|
<Project Path="samples/GettingStarted/DeclarativeAgents/Azure/DeclarativeAzureAgents.csproj" />
|
||||||
|
<Project Path="samples/GettingStarted/DeclarativeAgents/ChatClient/DeclarativeChatClientAgents.csproj" />
|
||||||
|
<Project Path="samples/GettingStarted/DeclarativeAgents/Foundry/DeclarativeFoundryAgents.csproj" />
|
||||||
|
<Project Path="samples/GettingStarted/DeclarativeAgents/FoundryPersistent/DeclarativeFoundryPersistentAgents.csproj" />
|
||||||
|
<Project Path="samples/GettingStarted/DeclarativeAgents/OpenAI/DeclarativeOpenAIAgents.csproj" />
|
||||||
|
</Folder>
|
||||||
<Folder Name="/Samples/Purview/" />
|
<Folder Name="/Samples/Purview/" />
|
||||||
<Folder Name="/Samples/Purview/AgentWithPurview/">
|
<Folder Name="/Samples/Purview/AgentWithPurview/">
|
||||||
<Project Path="samples/Purview/AgentWithPurview/AgentWithPurview.csproj" />
|
<Project Path="samples/Purview/AgentWithPurview/AgentWithPurview.csproj" />
|
||||||
@@ -338,6 +346,8 @@
|
|||||||
<Project Path="src/Microsoft.Agents.AI.AzureAI.Persistent/Microsoft.Agents.AI.AzureAI.Persistent.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.AzureAI/Microsoft.Agents.AI.AzureAI.csproj" />
|
||||||
<Project Path="src/Microsoft.Agents.AI.CopilotStudio/Microsoft.Agents.AI.CopilotStudio.csproj" />
|
<Project Path="src/Microsoft.Agents.AI.CopilotStudio/Microsoft.Agents.AI.CopilotStudio.csproj" />
|
||||||
|
<Project Path="src/Microsoft.Agents.AI.Declarative.AzureAI/Microsoft.Agents.AI.Declarative.AzureAI.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.DevUI/Microsoft.Agents.AI.DevUI.csproj" />
|
||||||
<Project Path="src/Microsoft.Agents.AI.DurableTask/Microsoft.Agents.AI.DurableTask.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" />
|
<Project Path="src/Microsoft.Agents.AI.Hosting.A2A.AspNetCore/Microsoft.Agents.AI.Hosting.A2A.AspNetCore.csproj" />
|
||||||
@@ -360,6 +370,7 @@
|
|||||||
<Project Path="tests/AzureAI.IntegrationTests/AzureAI.IntegrationTests.csproj" />
|
<Project Path="tests/AzureAI.IntegrationTests/AzureAI.IntegrationTests.csproj" />
|
||||||
<Project Path="tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistent.IntegrationTests.csproj" />
|
<Project Path="tests/AzureAIAgentsPersistent.IntegrationTests/AzureAIAgentsPersistent.IntegrationTests.csproj" />
|
||||||
<Project Path="tests/CopilotStudio.IntegrationTests/CopilotStudio.IntegrationTests.csproj" />
|
<Project Path="tests/CopilotStudio.IntegrationTests/CopilotStudio.IntegrationTests.csproj" />
|
||||||
|
<Project Path="tests/Microsoft.Agents.AI.Declarative.IntegrationTests/Microsoft.Agents.AI.Declarative.IntegrationTests.csproj" />
|
||||||
<Project Path="tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Microsoft.Agents.AI.DurableTask.IntegrationTests.csproj" />
|
<Project Path="tests/Microsoft.Agents.AI.DurableTask.IntegrationTests/Microsoft.Agents.AI.DurableTask.IntegrationTests.csproj" />
|
||||||
<Project Path="tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests.csproj" />
|
<Project Path="tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.IntegrationTests.csproj" />
|
||||||
<Project Path="tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests.csproj" />
|
<Project Path="tests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests/Microsoft.Agents.AI.Hosting.AzureFunctions.IntegrationTests.csproj" />
|
||||||
@@ -372,6 +383,7 @@
|
|||||||
<Folder Name="/Tests/UnitTests/">
|
<Folder Name="/Tests/UnitTests/">
|
||||||
<Project Path="tests/Microsoft.Agents.AI.A2A.UnitTests/Microsoft.Agents.AI.A2A.UnitTests.csproj" />
|
<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.Abstractions.UnitTests/Microsoft.Agents.AI.Abstractions.UnitTests.csproj" />
|
||||||
|
<Project Path="tests/Microsoft.Agents.AI.Declarative.UnitTests/Microsoft.Agents.AI.Declarative.UnitTests.csproj" />
|
||||||
<Project Path="tests/Microsoft.Agents.AI.AGUI.UnitTests/Microsoft.Agents.AI.AGUI.UnitTests.csproj" />
|
<Project Path="tests/Microsoft.Agents.AI.AGUI.UnitTests/Microsoft.Agents.AI.AGUI.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.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.AzureAI.UnitTests/Microsoft.Agents.AI.AzureAI.UnitTests.csproj" />
|
||||||
|
|||||||
+5
-1
@@ -3,10 +3,14 @@
|
|||||||
<packageSources>
|
<packageSources>
|
||||||
<clear />
|
<clear />
|
||||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||||
|
<add key="azure-sdk-for-net" value="https://pkgs.dev.azure.com/azure-sdk/public/_packaging/azure-sdk-for-net/nuget/v3/index.json" />
|
||||||
</packageSources>
|
</packageSources>
|
||||||
<packageSourceMapping>
|
<packageSourceMapping>
|
||||||
<packageSource key="nuget.org">
|
<packageSource key="nuget.org">
|
||||||
<package pattern="*" />
|
<package pattern="*" />
|
||||||
</packageSource>
|
</packageSource>
|
||||||
|
<packageSource key="azure-sdk-for-net">
|
||||||
|
<package pattern="Azure.AI.Agents" />
|
||||||
|
</packageSource>
|
||||||
</packageSourceMapping>
|
</packageSourceMapping>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<!-- Package validation. Baseline Version should be the latest version available on NuGet. -->
|
<!-- Package validation. Baseline Version should be the latest version available on NuGet. -->
|
||||||
<PackageValidationBaselineVersion>0.0.1</PackageValidationBaselineVersion>
|
<PackageValidationBaselineVersion>0.0.1</PackageValidationBaselineVersion>
|
||||||
<!-- Validate assembly attributes only for Publish builds -->
|
<!-- Validate assembly attributes only for Publish builds -->
|
||||||
<NoWarn Condition="'$(Configuration)' != 'Publish'">$(NoWarn);CP0003</NoWarn>
|
<NoWarn Condition="'$(Configuration)' != 'Publish'">$(NoWarn);CP0003;NU1900;NU1603</NoWarn>
|
||||||
<!-- Do not validate reference assemblies -->
|
<!-- Do not validate reference assemblies -->
|
||||||
<NoWarn>$(NoWarn);CP1002</NoWarn>
|
<NoWarn>$(NoWarn);CP1002</NoWarn>
|
||||||
|
|
||||||
|
|||||||
+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.AI.Agents" />
|
||||||
|
<PackageReference Include="Azure.Identity" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\..\src\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
// This sample shows how to create and use a AI agents with Azure Foundry Agents as the backend.
|
||||||
|
|
||||||
|
using Azure.AI.Agents;
|
||||||
|
using Azure.Identity;
|
||||||
|
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 agentsClient = new AgentClient(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, Temperature = 0.9f });
|
||||||
|
// 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 = agentsClient.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 = agentsClient.GetAIAgent(agentVersion);
|
||||||
|
|
||||||
|
// You can also create another AIAgent version (V2) by providing the same name with a different definition.
|
||||||
|
AIAgent jokerAgentV2 = agentsClient.CreateAIAgent(name: JokerName, model: deploymentName, instructions: JokerInstructions + "V2");
|
||||||
|
|
||||||
|
// You can also get the AIAgent latest version just providing its name.
|
||||||
|
AIAgent jokerAgentLatest = agentsClient.GetAIAgent(name: JokerName);
|
||||||
|
var latestVersion = jokerAgentLatest.GetService<AgentVersion>()!;
|
||||||
|
|
||||||
|
// The AIAgent version can be accessed via the GetService method.
|
||||||
|
Console.WriteLine($"Latest agent version id: {latestVersion.Id}");
|
||||||
|
|
||||||
|
var options = new ChatClientAgentRunOptions();
|
||||||
|
|
||||||
|
// Once you have the AIAgent, you can invoke it like any other AIAgent.
|
||||||
|
AgentThread thread = jokerAgentLatest.GetNewThread();
|
||||||
|
Console.WriteLine(await jokerAgentLatest.RunAsync("Tell me a joke about a pirate.", thread, options));
|
||||||
|
|
||||||
|
// 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, options));
|
||||||
|
|
||||||
|
// Cleanup by agent name removes both agent versions created (jokerAgentV1 + jokerAgentV2).
|
||||||
|
agentsClient.DeleteAgent(jokerAgentV1.Name);
|
||||||
|
|
||||||
|
// It is also possible delete just a specific agent version by the composition (name + version number).
|
||||||
|
// agentsClient.DeleteAgentVersion(latestVersion.Name, latestVersion.Version);
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Prerequisites
|
||||||
|
|
||||||
|
Before you begin, ensure you have the following prerequisites:
|
||||||
|
|
||||||
|
- .NET 8.0 SDK or later
|
||||||
|
- Azure Foundry service endpoint and deployment configured
|
||||||
|
- Azure CLI installed and authenticated (for Azure credential authentication)
|
||||||
|
|
||||||
|
**Note**: This demo uses Azure CLI credentials for authentication. 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).
|
||||||
|
|
||||||
|
Set the following environment variables:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-foundry-service.services.ai.azure.com/api/projects/your-foundry-project" # Replace with your Azure Foundry resource endpoint
|
||||||
|
$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o-mini" # Optional, defaults to gpt-4o-mini
|
||||||
|
```
|
||||||
+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 ChatClientAgentFactory(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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||||
|
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Azure.Identity" />
|
||||||
|
<PackageReference Include="Azure.AI.Agents.Persistent" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
|
<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.AzureAI\Microsoft.Agents.AI.Declarative.AzureAI.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// 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.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.");
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
var endpointUri = new Uri(endpoint);
|
||||||
|
var tokenCredential = new AzureCliCredential();
|
||||||
|
|
||||||
|
// Create the agent from the YAML definition.
|
||||||
|
var agentFactory = new AggregatorAgentFactory(
|
||||||
|
[
|
||||||
|
new OpenAIChatAgentFactory(endpointUri, tokenCredential),
|
||||||
|
new OpenAIResponseAgentFactory(endpointUri, tokenCredential),
|
||||||
|
new OpenAIAssistantAgentFactory(endpointUri, tokenCredential)
|
||||||
|
]);
|
||||||
|
var agent = await agentFactory.CreateFromYamlAsync(text);
|
||||||
|
|
||||||
|
// 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 agent run options
|
||||||
|
var options = new ChatClientAgentRunOptions(new()
|
||||||
|
{
|
||||||
|
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Invoke the agent and output the text result.
|
||||||
|
Console.WriteLine(await agent!.RunAsync(prompt, options: options));
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"Chat": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\azure\\AzureOpenAIChat.yaml \"What is the weather in Cambridge, MA in °C?\""
|
||||||
|
},
|
||||||
|
"Responses": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\azure\\AzureOpenAIResponses.yaml \"What is the weather in Cambridge, MA in °C?\""
|
||||||
|
},
|
||||||
|
"Assistants": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\azure\\AzureOpenAIAssistants.yaml \"What is the weather in Cambridge, MA in °C?\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+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 ChatClientAgentFactory(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.\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||||
|
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Azure.Identity" />
|
||||||
|
<PackageReference Include="Azure.AI.Agents.Persistent" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
|
<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.AzureAI\Microsoft.Agents.AI.Declarative.AzureAI.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
// This sample shows how to load an AI agent from a YAML file and process a prompt using Foundry Agents as the backend.
|
||||||
|
|
||||||
|
using Azure.Identity;
|
||||||
|
using Microsoft.Agents.AI;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Set up configuration with the Azure Foundry project endpoint
|
||||||
|
IConfiguration configuration = new ConfigurationBuilder()
|
||||||
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
["AZURE_FOUNDRY_PROJECT_ENDPOINT"] = endpoint
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Create the agent from the YAML definition.
|
||||||
|
var agentFactory = new FoundryAgentFactory(new AzureCliCredential(), configuration);
|
||||||
|
var agent = await agentFactory.CreateFromYamlAsync(text);
|
||||||
|
|
||||||
|
// Invoke the agent and output the text result.
|
||||||
|
Console.WriteLine(await agent!.RunAsync(prompt));
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"FoundryAgent": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\foundry\\FoundryAgent.yaml \"Tell me a recipe for chocolate chip cookies?\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||||
|
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Azure.Identity" />
|
||||||
|
<PackageReference Include="Azure.AI.Agents.Persistent" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
|
<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.AzureAI\Microsoft.Agents.AI.Declarative.AzureAI.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
// This sample shows how to load an AI agent from a YAML file and process a prompt using Foundry Agents as the backend.
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
using Azure.Identity;
|
||||||
|
using Microsoft.Agents.AI;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Set up configuration with the Azure Foundry project endpoint
|
||||||
|
IConfiguration configuration = new ConfigurationBuilder()
|
||||||
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
["AZURE_FOUNDRY_PROJECT_ENDPOINT"] = endpoint
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// 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 FoundryPersistentAgentFactory(new AzureCliCredential(), configuration);
|
||||||
|
var agent = await agentFactory.CreateFromYamlAsync(text);
|
||||||
|
|
||||||
|
// Create agent run options
|
||||||
|
var options = new ChatClientAgentRunOptions(new()
|
||||||
|
{
|
||||||
|
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Invoke the agent and output the text result.
|
||||||
|
Console.WriteLine(await agent!.RunAsync(prompt, options: options));
|
||||||
+8
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"FoundryAgent": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\foundry\\FoundryAgent.yaml \"What is the weather in Cambridge, MA in °C?\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFrameworks>net10.0</TargetFrameworks>
|
||||||
|
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Azure.Identity" />
|
||||||
|
<PackageReference Include="Azure.AI.Agents.Persistent" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||||
|
<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.AzureAI\Microsoft.Agents.AI.Declarative.AzureAI.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
// This sample shows how to load an AI agent from a YAML file and process a prompt using OpenAI as the backend.
|
||||||
|
|
||||||
|
using System.ComponentModel;
|
||||||
|
using Microsoft.Agents.AI;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
var apiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY") ?? throw new InvalidOperationException("OPENAI_APIKEY is not set.");
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Set up configuration with the OpenAI API key
|
||||||
|
IConfiguration configuration = new ConfigurationBuilder()
|
||||||
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
["OPENAI_APIKEY"] = apiKey
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Create the agent from the YAML definition.
|
||||||
|
var agentFactory = new AggregatorAgentFactory(
|
||||||
|
[
|
||||||
|
new OpenAIChatAgentFactory(configuration: configuration),
|
||||||
|
new OpenAIResponseAgentFactory(configuration: configuration),
|
||||||
|
new OpenAIAssistantAgentFactory(configuration: configuration)
|
||||||
|
]);
|
||||||
|
var agent = await agentFactory.CreateFromYamlAsync(text);
|
||||||
|
|
||||||
|
// 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 agent run options
|
||||||
|
var options = new ChatClientAgentRunOptions(new()
|
||||||
|
{
|
||||||
|
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Invoke the agent and output the text result.
|
||||||
|
Console.WriteLine(await agent!.RunAsync(prompt, options: options));
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"Chat": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\openai\\OpenAIChat.yaml \"What is the weather in Cambridge, MA in °C?\""
|
||||||
|
},
|
||||||
|
"Responses": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\openai\\OpenAIResponses.yaml \"What is the weather in Cambridge, MA in °C?\""
|
||||||
|
},
|
||||||
|
"Assistants": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"commandLineArgs": "..\\..\\..\\..\\..\\..\\..\\..\\agent-samples\\openai\\OpenAIAssistants.yaml \"What is the weather in Cambridge, MA in °C?\""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,32 +54,56 @@ internal sealed class Program
|
|||||||
agentDefinition: DefineResearchAgent(configuration),
|
agentDefinition: DefineResearchAgent(configuration),
|
||||||
agentDescription: "Planner agent for DeepResearch workflow");
|
agentDescription: "Planner agent for DeepResearch workflow");
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
await agentClient.CreateAgentAsync(
|
||||||
|
=======
|
||||||
await aiProjectClient.CreateAgentAsync(
|
await aiProjectClient.CreateAgentAsync(
|
||||||
|
>>>>>>> upstream/main
|
||||||
agentName: "PlannerAgent",
|
agentName: "PlannerAgent",
|
||||||
agentDefinition: DefinePlannerAgent(configuration),
|
agentDefinition: DefinePlannerAgent(configuration),
|
||||||
agentDescription: "Planner agent for DeepResearch workflow");
|
agentDescription: "Planner agent for DeepResearch workflow");
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
await agentClient.CreateAgentAsync(
|
||||||
|
=======
|
||||||
await aiProjectClient.CreateAgentAsync(
|
await aiProjectClient.CreateAgentAsync(
|
||||||
|
>>>>>>> upstream/main
|
||||||
agentName: "ManagerAgent",
|
agentName: "ManagerAgent",
|
||||||
agentDefinition: DefineManagerAgent(configuration),
|
agentDefinition: DefineManagerAgent(configuration),
|
||||||
agentDescription: "Manager agent for DeepResearch workflow");
|
agentDescription: "Manager agent for DeepResearch workflow");
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
await agentClient.CreateAgentAsync(
|
||||||
|
=======
|
||||||
await aiProjectClient.CreateAgentAsync(
|
await aiProjectClient.CreateAgentAsync(
|
||||||
|
>>>>>>> upstream/main
|
||||||
agentName: "SummaryAgent",
|
agentName: "SummaryAgent",
|
||||||
agentDefinition: DefineSummaryAgent(configuration),
|
agentDefinition: DefineSummaryAgent(configuration),
|
||||||
agentDescription: "Summary agent for DeepResearch workflow");
|
agentDescription: "Summary agent for DeepResearch workflow");
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
await agentClient.CreateAgentAsync(
|
||||||
|
=======
|
||||||
await aiProjectClient.CreateAgentAsync(
|
await aiProjectClient.CreateAgentAsync(
|
||||||
|
>>>>>>> upstream/main
|
||||||
agentName: "KnowledgeAgent",
|
agentName: "KnowledgeAgent",
|
||||||
agentDefinition: DefineKnowledgeAgent(configuration),
|
agentDefinition: DefineKnowledgeAgent(configuration),
|
||||||
agentDescription: "Research agent for DeepResearch workflow");
|
agentDescription: "Research agent for DeepResearch workflow");
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
await agentClient.CreateAgentAsync(
|
||||||
|
=======
|
||||||
await aiProjectClient.CreateAgentAsync(
|
await aiProjectClient.CreateAgentAsync(
|
||||||
|
>>>>>>> upstream/main
|
||||||
agentName: "CoderAgent",
|
agentName: "CoderAgent",
|
||||||
agentDefinition: DefineCoderAgent(configuration),
|
agentDefinition: DefineCoderAgent(configuration),
|
||||||
agentDescription: "Coder agent for DeepResearch workflow");
|
agentDescription: "Coder agent for DeepResearch workflow");
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
|
await agentClient.CreateAgentAsync(
|
||||||
|
=======
|
||||||
await aiProjectClient.CreateAgentAsync(
|
await aiProjectClient.CreateAgentAsync(
|
||||||
|
>>>>>>> upstream/main
|
||||||
agentName: "WeatherAgent",
|
agentName: "WeatherAgent",
|
||||||
agentDefinition: DefineWeatherAgent(configuration),
|
agentDefinition: DefineWeatherAgent(configuration),
|
||||||
agentDescription: "Weather agent for DeepResearch workflow");
|
agentDescription: "Weather agent for DeepResearch workflow");
|
||||||
@@ -272,10 +296,17 @@ internal sealed class Program
|
|||||||
Tools =
|
Tools =
|
||||||
{
|
{
|
||||||
AgentTool.CreateOpenApiTool(
|
AgentTool.CreateOpenApiTool(
|
||||||
|
<<<<<<< HEAD
|
||||||
|
new OpenApiFunctionDefinition(
|
||||||
|
"weather-forecast",
|
||||||
|
BinaryData.FromString(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "wttr.json"))),
|
||||||
|
new OpenApiAnonymousAuthDetails()))
|
||||||
|
=======
|
||||||
new OpenAPIFunctionDefinition(
|
new OpenAPIFunctionDefinition(
|
||||||
"weather-forecast",
|
"weather-forecast",
|
||||||
BinaryData.FromString(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "wttr.json"))),
|
BinaryData.FromString(File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "wttr.json"))),
|
||||||
new OpenAPIAnonymousAuthenticationDetails()))
|
new OpenAPIAnonymousAuthenticationDetails()))
|
||||||
|
>>>>>>> upstream/main
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<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.Agents.Persistent" />
|
||||||
|
<PackageReference Include="Azure.Identity" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\src\Microsoft.Agents.AI.AzureAI.Persistent\Microsoft.Agents.AI.AzureAI.Persistent.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
// This sample shows how to create an Azure AI Foundry Agent with the Deep Research Tool.
|
||||||
|
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Azure.Identity;
|
||||||
|
using Microsoft.Agents.AI;
|
||||||
|
|
||||||
|
var endpoint = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_FOUNDRY_PROJECT_ENDPOINT is not set.");
|
||||||
|
var deepResearchDeploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEEP_RESEARCH_DEPLOYMENT_NAME") ?? "o3-deep-research";
|
||||||
|
var modelDeploymentName = Environment.GetEnvironmentVariable("AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME") ?? "gpt-4o";
|
||||||
|
var bingConnectionId = Environment.GetEnvironmentVariable("BING_CONNECTION_ID") ?? throw new InvalidOperationException("BING_CONNECTION_ID is not set.");
|
||||||
|
|
||||||
|
// Configure extended network timeout for long-running Deep Research tasks.
|
||||||
|
PersistentAgentsAdministrationClientOptions persistentAgentsClientOptions = new();
|
||||||
|
persistentAgentsClientOptions.Retry.NetworkTimeout = TimeSpan.FromMinutes(20);
|
||||||
|
|
||||||
|
// Get a client to create/retrieve server side agents with.
|
||||||
|
PersistentAgentsClient persistentAgentsClient = new(endpoint, new AzureCliCredential(), persistentAgentsClientOptions);
|
||||||
|
|
||||||
|
// Define and configure the Deep Research tool.
|
||||||
|
DeepResearchToolDefinition deepResearchTool = new(new DeepResearchDetails(
|
||||||
|
bingGroundingConnections: [new(bingConnectionId)],
|
||||||
|
model: deepResearchDeploymentName)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create an agent with the Deep Research tool on the Azure AI agent service.
|
||||||
|
AIAgent agent = await persistentAgentsClient.CreateAIAgentAsync(
|
||||||
|
model: modelDeploymentName,
|
||||||
|
name: "DeepResearchAgent",
|
||||||
|
instructions: "You are a helpful Agent that assists in researching scientific topics.",
|
||||||
|
tools: [deepResearchTool]);
|
||||||
|
|
||||||
|
const string Task = "Research the current state of studies on orca intelligence and orca language, " +
|
||||||
|
"including what is currently known about orcas' cognitive capabilities and communication systems.";
|
||||||
|
|
||||||
|
Console.WriteLine($"# User: '{Task}'");
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
AgentThread thread = agent.GetNewThread();
|
||||||
|
|
||||||
|
await foreach (var response in agent.RunStreamingAsync(Task, thread))
|
||||||
|
{
|
||||||
|
Console.Write(response.Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# What this sample demonstrates
|
||||||
|
|
||||||
|
This sample demonstrates how to create an Azure AI Agent with the Deep Research Tool, which leverages the o3-deep-research reasoning model to perform comprehensive research on complex topics.
|
||||||
|
|
||||||
|
Key features:
|
||||||
|
- Configuring and using the Deep Research Tool with Bing grounding
|
||||||
|
- Creating a persistent AI agent with deep research capabilities
|
||||||
|
- Executing deep research queries and retrieving results
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before running this sample, ensure you have:
|
||||||
|
|
||||||
|
1. An Azure AI Foundry project set up
|
||||||
|
2. A deep research model deployment (e.g., o3-deep-research)
|
||||||
|
3. A model deployment (e.g., gpt-4o)
|
||||||
|
4. A Bing Connection configured in your Azure AI Foundry project
|
||||||
|
5. Azure CLI installed and authenticated
|
||||||
|
|
||||||
|
**Important**: Please visit the following documentation for detailed setup instructions:
|
||||||
|
- [Deep Research Tool Documentation](https://aka.ms/agents-deep-research)
|
||||||
|
- [Research Tool Setup](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/how-to/tools/deep-research#research-tool-setup)
|
||||||
|
|
||||||
|
Pay special attention to the purple `Note` boxes in the Azure documentation.
|
||||||
|
|
||||||
|
**Note**: The Bing Connection ID must be from the **project**, not the resource. It has the following format:
|
||||||
|
|
||||||
|
```
|
||||||
|
/subscriptions/<sub_id>/resourceGroups/<rg_name>/providers/<provider_name>/accounts/<account_name>/projects/<project_name>/connections/<connection_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
Set the following environment variables:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Replace with your Azure AI Foundry project endpoint
|
||||||
|
$env:AZURE_FOUNDRY_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com/"
|
||||||
|
|
||||||
|
# Replace with your Bing connection ID from the project
|
||||||
|
$env:BING_CONNECTION_ID="/subscriptions/.../connections/your-bing-connection"
|
||||||
|
|
||||||
|
# Optional, defaults to o3-deep-research
|
||||||
|
$env:AZURE_FOUNDRY_PROJECT_DEEP_RESEARCH_DEPLOYMENT_NAME="o3-deep-research"
|
||||||
|
|
||||||
|
# Optional, defaults to gpt-4o
|
||||||
|
$env:AZURE_FOUNDRY_PROJECT_DEPLOYMENT_NAME="gpt-4o"
|
||||||
@@ -0,0 +1,916 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.ClientModel;
|
||||||
|
using System.ClientModel.Primitives;
|
||||||
|
using Microsoft.Agents.AI;
|
||||||
|
using Microsoft.Agents.AI.AzureAI;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
using OpenAI;
|
||||||
|
using OpenAI.Responses;
|
||||||
|
|
||||||
|
#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.
|
||||||
|
#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
|
|
||||||
|
namespace Azure.AI.Agents;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for <see cref="AgentClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class AgentClientExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AgentClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The <see cref="AgentClient"/> to create the <see cref="ChatClientAgent"/> with. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="agentReference">The <see cref="AgentReference"/> representing the name and version of the server side agent to create a <see cref="ChatClientAgent"/> for. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="tools">The tools to use when interacting with the agent. This is required when using prompt agent definitions with tools.</param>
|
||||||
|
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations based on the latest version of the named Azure AI Agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/> or <paramref name="agentReference"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <exception cref="InvalidOperationException">The agent with the specified name was not found.</exception>
|
||||||
|
/// <remarks>
|
||||||
|
/// When retrieving an agent by using an <see cref="AgentReference"/>, minimal information will be available about the agent in the instance level, and any logic that relies
|
||||||
|
/// on <see cref="AIAgent.GetService(Type, object?)"/> to retrieve information about the agent like <see cref="AgentVersion" /> will receive <see langword="null"/> as the result.
|
||||||
|
/// </remarks>
|
||||||
|
public static ChatClientAgent GetAIAgent(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
AgentReference agentReference,
|
||||||
|
IList<AITool>? tools = null,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
IServiceProvider? services = null)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNull(agentReference);
|
||||||
|
|
||||||
|
return CreateChatClientAgent(
|
||||||
|
agentClient,
|
||||||
|
agentReference,
|
||||||
|
new ChatClientAgentOptions()
|
||||||
|
{
|
||||||
|
Id = $"{agentReference.Name}:{agentReference.Version}",
|
||||||
|
Name = agentReference.Name,
|
||||||
|
ChatOptions = new() { Tools = tools },
|
||||||
|
},
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AgentClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The <see cref="AgentClient"/> to create the <see cref="ChatClientAgent"/> with. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="name">The name of the server side agent to create a <see cref="ChatClientAgent"/> for. Cannot be <see langword="null"/> or whitespace.</param>
|
||||||
|
/// <param name="tools">The tools to use when interacting with the agent. This is required when using prompt agent definitions with tools.</param>
|
||||||
|
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||||
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations based on the latest version of the named Azure AI Agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/> or <paramref name="name"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when <paramref name="name"/> is empty or whitespace, or when the agent with the specified name was not found.</exception>
|
||||||
|
/// <exception cref="InvalidOperationException">The agent with the specified name was not found.</exception>
|
||||||
|
public static ChatClientAgent GetAIAgent(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
string name,
|
||||||
|
IList<AITool>? tools = null,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
IServiceProvider? services = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNullOrWhitespace(name);
|
||||||
|
|
||||||
|
AgentRecord agentRecord = GetAgentRecordByName(agentClient, name, cancellationToken);
|
||||||
|
|
||||||
|
return GetAIAgent(
|
||||||
|
agentClient,
|
||||||
|
agentRecord,
|
||||||
|
tools,
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously retrieves an existing server side agent, wrapped as a <see cref="ChatClientAgent"/> using the provided <see cref="AgentClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The <see cref="AgentClient"/> to create the <see cref="ChatClientAgent"/> with. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="name">The name of the server side agent to create a <see cref="ChatClientAgent"/> for. Cannot be <see langword="null"/> or whitespace.</param>
|
||||||
|
/// <param name="tools">The tools to use when interacting with the agent. This is required when using prompt agent definitions with tools.</param>
|
||||||
|
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||||
|
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to monitor for cancellation requests. The default is <see cref="CancellationToken.None"/>.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations based on the latest version of the named Azure AI Agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/> or <paramref name="name"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when <paramref name="name"/> is empty or whitespace, or when the agent with the specified name was not found.</exception>
|
||||||
|
/// <exception cref="InvalidOperationException">The agent with the specified name was not found.</exception>
|
||||||
|
public static async Task<ChatClientAgent> GetAIAgentAsync(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
string name,
|
||||||
|
IList<AITool>? tools = null,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
IServiceProvider? services = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNullOrWhitespace(name);
|
||||||
|
|
||||||
|
AgentRecord agentRecord = await GetAgentRecordByNameAsync(agentClient, name, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return GetAIAgent(
|
||||||
|
agentClient,
|
||||||
|
agentRecord,
|
||||||
|
tools,
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a runnable agent instance from the provided agent record.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The client used to interact with Azure AI Agents. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="agentRecord">The agent record to be converted. The latest version will be used. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="tools">The tools to use when interacting with the agent. This is required when using prompt agent definitions with tools.</param>
|
||||||
|
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations based on the latest version of the Azure AI Agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/> or <paramref name="agentRecord"/> is <see langword="null"/>.</exception>
|
||||||
|
public static ChatClientAgent GetAIAgent(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
AgentRecord agentRecord,
|
||||||
|
IList<AITool>? tools = null,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
IServiceProvider? services = null)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNull(agentRecord);
|
||||||
|
|
||||||
|
var allowDeclarativeMode = tools is not { Count: > 0 };
|
||||||
|
|
||||||
|
return CreateChatClientAgent(
|
||||||
|
agentClient,
|
||||||
|
agentRecord,
|
||||||
|
tools,
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
!allowDeclarativeMode,
|
||||||
|
services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a runnable agent instance from a <see cref="AgentVersion"/> containing metadata about an Azure AI Agent.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The client used to interact with Azure AI Agents. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="agentVersion">The agent version to be converted. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="tools">In-process invocable tools to be provided. If no tools are provided manual handling will be necessary to invoke in-process tools.</param>
|
||||||
|
/// <param name="clientFactory">Provides a way to customize the creation of the underlying <see cref="IChatClient"/> used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations based on the provided version of the Azure AI Agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/> or <paramref name="agentVersion"/> is <see langword="null"/>.</exception>
|
||||||
|
public static ChatClientAgent GetAIAgent(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
AgentVersion agentVersion,
|
||||||
|
IList<AITool>? tools = null,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
IServiceProvider? services = null)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNull(agentVersion);
|
||||||
|
|
||||||
|
var allowDeclarativeMode = tools is not { Count: > 0 };
|
||||||
|
|
||||||
|
return CreateChatClientAgent(
|
||||||
|
agentClient,
|
||||||
|
agentVersion,
|
||||||
|
tools,
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
!allowDeclarativeMode,
|
||||||
|
services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new Prompt AI Agent using the provided <see cref="AgentClient"/> and options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="options">The options for creating the agent. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||||
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to cancel the operation if needed.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||||
|
public static ChatClientAgent GetAIAgent(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
ChatClientAgentOptions options,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
IServiceProvider? services = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNull(options);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(options.Name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Agent name must be provided in the options.Name property", nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
AgentRecord agentRecord = GetAgentRecordByName(agentClient, options.Name, cancellationToken);
|
||||||
|
var agentVersion = agentRecord.Versions.Latest;
|
||||||
|
|
||||||
|
var agentOptions = CreateChatClientAgentOptions(agentVersion, options, requireInvocableTools: true);
|
||||||
|
|
||||||
|
return CreateChatClientAgent(
|
||||||
|
agentClient,
|
||||||
|
agentVersion,
|
||||||
|
agentOptions,
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new Prompt AI Agent using the provided <see cref="AgentClient"/> and options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="options">The options for creating the agent. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||||
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to cancel the operation if needed.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||||
|
public static async Task<ChatClientAgent> GetAIAgentAsync(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
ChatClientAgentOptions options,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
IServiceProvider? services = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNull(options);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(options.Name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Agent name must be provided in the options.Name property", nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
AgentRecord agentRecord = await GetAgentRecordByNameAsync(agentClient, options.Name, cancellationToken).ConfigureAwait(false);
|
||||||
|
var agentVersion = agentRecord.Versions.Latest;
|
||||||
|
|
||||||
|
var agentOptions = CreateChatClientAgentOptions(agentVersion, options, requireInvocableTools: true);
|
||||||
|
|
||||||
|
return CreateChatClientAgent(
|
||||||
|
agentClient,
|
||||||
|
agentVersion,
|
||||||
|
agentOptions,
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new Prompt AI agent using the specified configuration parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="name">The name for the agent.</param>
|
||||||
|
/// <param name="model">The name of the model to use for the agent. Cannot be <see langword="null"/> or whitespace.</param>
|
||||||
|
/// <param name="instructions">The instructions that guide the agent's behavior. Cannot be <see langword="null"/> or whitespace.</param>
|
||||||
|
/// <param name="description">The description for the agent.</param>
|
||||||
|
/// <param name="tools">The tools to use when interacting with the agent, this is required when using prompt agent definitions with tools.</param>
|
||||||
|
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||||
|
/// <param name="cancellationToken">A token to monitor for cancellation requests.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/>, <paramref name="model"/>, or <paramref name="instructions"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> or <paramref name="instructions"/> is empty or whitespace.</exception>
|
||||||
|
/// <remarks>When using prompt agent definitions with tools the parameter <paramref name="tools"/> needs to be provided.</remarks>
|
||||||
|
public static ChatClientAgent CreateAIAgent(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
string name,
|
||||||
|
string model,
|
||||||
|
string instructions,
|
||||||
|
string? description = null,
|
||||||
|
IList<AITool>? tools = null,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
IServiceProvider? services = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNullOrWhitespace(name);
|
||||||
|
Throw.IfNullOrWhitespace(model);
|
||||||
|
Throw.IfNullOrWhitespace(instructions);
|
||||||
|
|
||||||
|
return CreateAIAgent(
|
||||||
|
agentClient,
|
||||||
|
name,
|
||||||
|
tools,
|
||||||
|
new AgentVersionCreationOptions(new PromptAgentDefinition(model) { Instructions = instructions }) { Description = description },
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services,
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new Prompt AI agent using the specified configuration parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="name">The name for the agent.</param>
|
||||||
|
/// <param name="model">The name of the model to use for the agent. Cannot be <see langword="null"/> or whitespace.</param>
|
||||||
|
/// <param name="instructions">The instructions that guide the agent's behavior. Cannot be <see langword="null"/> or whitespace.</param>
|
||||||
|
/// <param name="description">The description for the agent.</param>
|
||||||
|
/// <param name="tools">The tools to use when interacting with the agent, this is required when using prompt agent definitions with tools.</param>
|
||||||
|
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||||
|
/// <param name="cancellationToken">A token to monitor for cancellation requests.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/>, <paramref name="model"/>, or <paramref name="instructions"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> or <paramref name="instructions"/> is empty or whitespace.</exception>
|
||||||
|
/// <remarks>When using prompt agent definitions with tools the parameter <paramref name="tools"/> needs to be provided.</remarks>
|
||||||
|
public static Task<ChatClientAgent> CreateAIAgentAsync(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
string name,
|
||||||
|
string model,
|
||||||
|
string instructions,
|
||||||
|
string? description = null,
|
||||||
|
IList<AITool>? tools = null,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
IServiceProvider? services = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNullOrWhitespace(name);
|
||||||
|
Throw.IfNullOrWhitespace(model);
|
||||||
|
Throw.IfNullOrWhitespace(instructions);
|
||||||
|
|
||||||
|
return CreateAIAgentAsync(
|
||||||
|
agentClient,
|
||||||
|
name,
|
||||||
|
tools,
|
||||||
|
new AgentVersionCreationOptions(new PromptAgentDefinition(model) { Instructions = instructions }) { Description = description },
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services,
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new Prompt AI Agent using the provided <see cref="AgentClient"/> and options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="model">The name of the model to use for the agent. Cannot be <see langword="null"/> or whitespace.</param>
|
||||||
|
/// <param name="options">The options for creating the agent. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||||
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to cancel the operation if needed.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> is empty or whitespace, or when the agent name is not provided in the options.</exception>
|
||||||
|
public static ChatClientAgent CreateAIAgent(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
string model,
|
||||||
|
ChatClientAgentOptions options,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
IServiceProvider? services = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNull(options);
|
||||||
|
Throw.IfNullOrWhitespace(model);
|
||||||
|
const bool RequireInvocableTools = true;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(options.Name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Agent name must be provided in the options.Name property", nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
PromptAgentDefinition agentDefinition = new(model)
|
||||||
|
{
|
||||||
|
Instructions = options.Instructions,
|
||||||
|
};
|
||||||
|
|
||||||
|
ApplyToolsToAgentDefinition(agentDefinition, options.ChatOptions?.Tools);
|
||||||
|
|
||||||
|
AgentVersionCreationOptions? creationOptions = new(agentDefinition);
|
||||||
|
if (!string.IsNullOrWhiteSpace(options.Description))
|
||||||
|
{
|
||||||
|
creationOptions.Description = options.Description;
|
||||||
|
}
|
||||||
|
|
||||||
|
AgentVersion agentVersion = CreateAgentVersionWithProtocol(agentClient, options.Name, creationOptions, cancellationToken);
|
||||||
|
|
||||||
|
var agentOptions = CreateChatClientAgentOptions(agentVersion, options, RequireInvocableTools);
|
||||||
|
|
||||||
|
return CreateChatClientAgent(
|
||||||
|
agentClient,
|
||||||
|
agentVersion,
|
||||||
|
agentOptions,
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new Prompt AI Agent using the provided <see cref="AgentClient"/> and options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="model">The name of the model to use for the agent. Cannot be <see langword="null"/> or whitespace.</param>
|
||||||
|
/// <param name="options">The options for creating the agent. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="services">An optional <see cref="IServiceProvider"/> to use for resolving services required by the <see cref="AIFunction"/> instances being invoked.</param>
|
||||||
|
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to cancel the operation if needed.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/> or <paramref name="options"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <exception cref="ArgumentException">Thrown when <paramref name="model"/> is empty or whitespace, or when the agent name is not provided in the options.</exception>
|
||||||
|
public static async Task<ChatClientAgent> CreateAIAgentAsync(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
string model,
|
||||||
|
ChatClientAgentOptions options,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
IServiceProvider? services = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNull(options);
|
||||||
|
Throw.IfNullOrWhitespace(model);
|
||||||
|
const bool RequireInvocableTools = true;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(options.Name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Agent name must be provided in the options.Name property", nameof(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
PromptAgentDefinition agentDefinition = new(model)
|
||||||
|
{
|
||||||
|
Instructions = options.Instructions,
|
||||||
|
};
|
||||||
|
|
||||||
|
ApplyToolsToAgentDefinition(agentDefinition, options.ChatOptions?.Tools);
|
||||||
|
|
||||||
|
AgentVersionCreationOptions? creationOptions = new(agentDefinition);
|
||||||
|
if (!string.IsNullOrWhiteSpace(options.Description))
|
||||||
|
{
|
||||||
|
creationOptions.Description = options.Description;
|
||||||
|
}
|
||||||
|
|
||||||
|
AgentVersion agentVersion = await CreateAgentVersionWithProtocolAsync(agentClient, options.Name, creationOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
var agentOptions = CreateChatClientAgentOptions(agentVersion, options, RequireInvocableTools);
|
||||||
|
|
||||||
|
return CreateChatClientAgent(
|
||||||
|
agentClient,
|
||||||
|
agentVersion,
|
||||||
|
agentOptions,
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new AI agent using the specified agent definition and optional configuration parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="name">The name for the agent.</param>
|
||||||
|
/// <param name="creationOptions">Settings that control the creation of the agent.</param>
|
||||||
|
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="cancellationToken">A token to monitor for cancellation requests.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/> or <paramref name="creationOptions"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <remarks>
|
||||||
|
/// When using this extension method with a <see cref="PromptAgentDefinition"/> the tools are only declarative and not invocable.
|
||||||
|
/// Invocation of any in-process tools will need to be handled manually.
|
||||||
|
/// </remarks>
|
||||||
|
public static ChatClientAgent CreateAIAgent(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
string name,
|
||||||
|
AgentVersionCreationOptions creationOptions,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNullOrWhitespace(name);
|
||||||
|
Throw.IfNull(creationOptions);
|
||||||
|
|
||||||
|
return CreateAIAgent(
|
||||||
|
agentClient,
|
||||||
|
name,
|
||||||
|
tools: null,
|
||||||
|
creationOptions,
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services: null,
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously creates a new AI agent using the specified agent definition and optional configuration
|
||||||
|
/// parameters.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The client used to manage and interact with AI agents. Cannot be <see langword="null"/>.</param>
|
||||||
|
/// <param name="name">The name for the agent.</param>
|
||||||
|
/// <param name="creationOptions">Settings that control the creation of the agent.</param>
|
||||||
|
/// <param name="clientFactory">A factory function to customize the creation of the chat client used by the agent.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <param name="cancellationToken">A token to monitor for cancellation requests.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgent"/> instance that can be used to perform operations on the newly created agent.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Thrown when <paramref name="agentClient"/> or <paramref name="creationOptions"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <remarks>
|
||||||
|
/// When using this extension method with a <see cref="PromptAgentDefinition"/> the tools are only declarative and not invocable.
|
||||||
|
/// Invocation of any in-process tools will need to be handled manually.
|
||||||
|
/// </remarks>
|
||||||
|
public static Task<ChatClientAgent> CreateAIAgentAsync(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
string name,
|
||||||
|
AgentVersionCreationOptions creationOptions,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory = null,
|
||||||
|
OpenAIClientOptions? openAIClientOptions = null,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNullOrWhitespace(name);
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNull(creationOptions);
|
||||||
|
|
||||||
|
return CreateAIAgentAsync(
|
||||||
|
agentClient,
|
||||||
|
name,
|
||||||
|
tools: null,
|
||||||
|
creationOptions,
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services: null,
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Private
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves an agent record by name using the Protocol method with user-agent header.
|
||||||
|
/// </summary>
|
||||||
|
private static AgentRecord GetAgentRecordByName(AgentClient agentClient, string agentName, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
ClientResult protocolResponse = agentClient.GetAgent(agentName, cancellationToken.ToRequestOptions(false));
|
||||||
|
return ClientResult.FromOptionalValue((AgentRecord)protocolResponse, protocolResponse.GetRawResponse()).Value
|
||||||
|
?? throw new InvalidOperationException($"Agent with name '{agentName}' not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously retrieves an agent record by name using the Protocol method with user-agent header.
|
||||||
|
/// </summary>
|
||||||
|
private static async Task<AgentRecord> GetAgentRecordByNameAsync(AgentClient agentClient, string agentName, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
ClientResult protocolResponse = await agentClient.GetAgentAsync(agentName, cancellationToken.ToRequestOptions(false)).ConfigureAwait(false);
|
||||||
|
return ClientResult.FromOptionalValue((AgentRecord)protocolResponse, protocolResponse.GetRawResponse()).Value
|
||||||
|
?? throw new InvalidOperationException($"Agent with name '{agentName}' not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an agent version using the Protocol method with user-agent header.
|
||||||
|
/// </summary>
|
||||||
|
private static AgentVersion CreateAgentVersionWithProtocol(AgentClient agentClient, string agentName, AgentVersionCreationOptions creationOptions, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
using BinaryContent protocolRequest = BinaryContent.Create(ModelReaderWriter.Write(creationOptions, ModelReaderWriterOptions.Json, AzureAIAgentsContext.Default));
|
||||||
|
ClientResult protocolResponse = agentClient.CreateAgentVersion(agentName, protocolRequest, cancellationToken.ToRequestOptions(false));
|
||||||
|
return ClientResult.FromValue((AgentVersion)protocolResponse, protocolResponse.GetRawResponse()).Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously creates an agent version using the Protocol method with user-agent header.
|
||||||
|
/// </summary>
|
||||||
|
private static async Task<AgentVersion> CreateAgentVersionWithProtocolAsync(AgentClient agentClient, string agentName, AgentVersionCreationOptions creationOptions, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
using BinaryContent protocolRequest = BinaryContent.Create(ModelReaderWriter.Write(creationOptions, ModelReaderWriterOptions.Json, AzureAIAgentsContext.Default));
|
||||||
|
ClientResult protocolResponse = await agentClient.CreateAgentVersionAsync(agentName, protocolRequest, cancellationToken.ToRequestOptions(false)).ConfigureAwait(false);
|
||||||
|
return ClientResult.FromValue((AgentVersion)protocolResponse, protocolResponse.GetRawResponse()).Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ChatClientAgent CreateAIAgent(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
string name,
|
||||||
|
IList<AITool>? tools,
|
||||||
|
AgentVersionCreationOptions creationOptions,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory,
|
||||||
|
OpenAIClientOptions? openAIClientOptions,
|
||||||
|
IServiceProvider? services,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNullOrWhitespace(name);
|
||||||
|
Throw.IfNull(creationOptions);
|
||||||
|
|
||||||
|
var allowDeclarativeMode = tools is not { Count: > 0 };
|
||||||
|
|
||||||
|
if (!allowDeclarativeMode)
|
||||||
|
{
|
||||||
|
ApplyToolsToAgentDefinition(creationOptions.Definition, tools);
|
||||||
|
}
|
||||||
|
|
||||||
|
AgentVersion agentVersion = CreateAgentVersionWithProtocol(agentClient, name, creationOptions, cancellationToken);
|
||||||
|
|
||||||
|
return CreateChatClientAgent(
|
||||||
|
agentClient,
|
||||||
|
agentVersion,
|
||||||
|
tools,
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
!allowDeclarativeMode,
|
||||||
|
services);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<ChatClientAgent> CreateAIAgentAsync(
|
||||||
|
this AgentClient agentClient,
|
||||||
|
string name,
|
||||||
|
IList<AITool>? tools,
|
||||||
|
AgentVersionCreationOptions creationOptions,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory,
|
||||||
|
OpenAIClientOptions? openAIClientOptions,
|
||||||
|
IServiceProvider? services,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
Throw.IfNullOrWhitespace(name);
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
Throw.IfNull(creationOptions);
|
||||||
|
|
||||||
|
var allowDeclarativeMode = tools is not { Count: > 0 };
|
||||||
|
|
||||||
|
if (!allowDeclarativeMode)
|
||||||
|
{
|
||||||
|
ApplyToolsToAgentDefinition(creationOptions.Definition, tools);
|
||||||
|
}
|
||||||
|
|
||||||
|
AgentVersion agentVersion = await CreateAgentVersionWithProtocolAsync(agentClient, name, creationOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return CreateChatClientAgent(
|
||||||
|
agentClient,
|
||||||
|
agentVersion,
|
||||||
|
tools,
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
!allowDeclarativeMode,
|
||||||
|
services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>This method creates an <see cref="ChatClientAgent"/> with the specified ChatClientAgentOptions.</summary>
|
||||||
|
private static ChatClientAgent CreateChatClientAgent(
|
||||||
|
AgentClient agentClient,
|
||||||
|
AgentVersion agentVersion,
|
||||||
|
ChatClientAgentOptions agentOptions,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory,
|
||||||
|
OpenAIClientOptions? openAIClientOptions,
|
||||||
|
IServiceProvider? services)
|
||||||
|
{
|
||||||
|
IChatClient chatClient = new AzureAIAgentChatClient(agentClient, agentVersion, agentOptions.ChatOptions, openAIClientOptions);
|
||||||
|
|
||||||
|
if (clientFactory is not null)
|
||||||
|
{
|
||||||
|
chatClient = clientFactory(chatClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ChatClientAgent(chatClient, agentOptions, services: services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>This method creates an <see cref="ChatClientAgent"/> with the specified ChatClientAgentOptions.</summary>
|
||||||
|
private static ChatClientAgent CreateChatClientAgent(
|
||||||
|
AgentClient agentClient,
|
||||||
|
AgentRecord agentRecord,
|
||||||
|
ChatClientAgentOptions agentOptions,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory,
|
||||||
|
OpenAIClientOptions? openAIClientOptions,
|
||||||
|
IServiceProvider? services)
|
||||||
|
{
|
||||||
|
IChatClient chatClient = new AzureAIAgentChatClient(agentClient, agentRecord, agentOptions.ChatOptions, openAIClientOptions);
|
||||||
|
|
||||||
|
if (clientFactory is not null)
|
||||||
|
{
|
||||||
|
chatClient = clientFactory(chatClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ChatClientAgent(chatClient, agentOptions, services: services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>This method creates an <see cref="ChatClientAgent"/> with the specified ChatClientAgentOptions.</summary>
|
||||||
|
private static ChatClientAgent CreateChatClientAgent(
|
||||||
|
AgentClient agentClient,
|
||||||
|
AgentReference agentReference,
|
||||||
|
ChatClientAgentOptions agentOptions,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory,
|
||||||
|
OpenAIClientOptions? openAIClientOptions,
|
||||||
|
IServiceProvider? services)
|
||||||
|
{
|
||||||
|
IChatClient chatClient = new AzureAIAgentChatClient(agentClient, agentReference, defaultModelId: null, agentOptions.ChatOptions, openAIClientOptions);
|
||||||
|
|
||||||
|
if (clientFactory is not null)
|
||||||
|
{
|
||||||
|
chatClient = clientFactory(chatClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ChatClientAgent(chatClient, agentOptions, services: services);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>This method creates an <see cref="ChatClientAgent"/> with a auto-generated ChatClientAgentOptions from the specified configuration parameters.</summary>
|
||||||
|
private static ChatClientAgent CreateChatClientAgent(
|
||||||
|
AgentClient AgentClient,
|
||||||
|
AgentVersion agentVersion,
|
||||||
|
IList<AITool>? tools,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory,
|
||||||
|
OpenAIClientOptions? openAIClientOptions,
|
||||||
|
bool requireInvocableTools,
|
||||||
|
IServiceProvider? services)
|
||||||
|
=> CreateChatClientAgent(
|
||||||
|
AgentClient,
|
||||||
|
agentVersion,
|
||||||
|
CreateChatClientAgentOptions(agentVersion, new ChatOptions() { Tools = tools }, requireInvocableTools),
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services);
|
||||||
|
|
||||||
|
/// <summary>This method creates an <see cref="ChatClientAgent"/> with a auto-generated ChatClientAgentOptions from the specified configuration parameters.</summary>
|
||||||
|
private static ChatClientAgent CreateChatClientAgent(
|
||||||
|
AgentClient AgentClient,
|
||||||
|
AgentRecord agentRecord,
|
||||||
|
IList<AITool>? tools,
|
||||||
|
Func<IChatClient, IChatClient>? clientFactory,
|
||||||
|
OpenAIClientOptions? openAIClientOptions,
|
||||||
|
bool requireInvocableTools,
|
||||||
|
IServiceProvider? services)
|
||||||
|
=> CreateChatClientAgent(
|
||||||
|
AgentClient,
|
||||||
|
agentRecord,
|
||||||
|
CreateChatClientAgentOptions(agentRecord.Versions.Latest, new ChatOptions() { Tools = tools }, requireInvocableTools),
|
||||||
|
clientFactory,
|
||||||
|
openAIClientOptions,
|
||||||
|
services);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method creates <see cref="ChatClientAgentOptions"/> for the specified <see cref="AgentVersion"/> and the provided tools.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentVersion">The agent version.</param>
|
||||||
|
/// <param name="chatOptions">The <see cref="ChatOptions"/> to use when interacting with the agent.</param>
|
||||||
|
/// <param name="requireInvocableTools">Indicates whether to enforce the presence of invocable tools when the AIAgent is created with an agent definition that uses them.</param>
|
||||||
|
/// <returns>The created <see cref="ChatClientAgentOptions"/>.</returns>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown when the agent definition requires in-process tools but none were provided.</exception>
|
||||||
|
/// <exception cref="InvalidOperationException">Thrown when the agent definition required tools were not provided.</exception>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method rebuilds the agent options from the agent definition returned by the version and combine with the in-proc tools when provided
|
||||||
|
/// this ensures that all required tools are provided and the definition of the agent options are consistent with the agent definition coming from the server.
|
||||||
|
/// </remarks>
|
||||||
|
private static ChatClientAgentOptions CreateChatClientAgentOptions(AgentVersion agentVersion, ChatOptions? chatOptions, bool requireInvocableTools)
|
||||||
|
{
|
||||||
|
var agentDefinition = agentVersion.Definition;
|
||||||
|
|
||||||
|
List<AITool>? agentTools = null;
|
||||||
|
if (agentDefinition is PromptAgentDefinition { Tools: { Count: > 0 } definitionTools })
|
||||||
|
{
|
||||||
|
// Check if no tools were provided while the agent definition requires in-proc tools.
|
||||||
|
if (requireInvocableTools && chatOptions?.Tools is not { Count: > 0 } && definitionTools.Any(t => t is FunctionTool))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The agent definition in-process tools must be provided in the extension method tools parameter.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agregate all missing tools for a single error message.
|
||||||
|
List<string>? missingTools = null;
|
||||||
|
|
||||||
|
// Check function tools
|
||||||
|
foreach (ResponseTool responseTool in definitionTools)
|
||||||
|
{
|
||||||
|
if (requireInvocableTools && responseTool is FunctionTool functionTool)
|
||||||
|
{
|
||||||
|
// Check if a tool with the same type and name exists in the provided tools.
|
||||||
|
// When invocable tools are required, match only AIFunction.
|
||||||
|
var matchingTool = chatOptions?.Tools?.FirstOrDefault(t => t is AIFunction tf && functionTool.FunctionName == tf.Name);
|
||||||
|
|
||||||
|
if (matchingTool is null)
|
||||||
|
{
|
||||||
|
(missingTools ??= []).Add($"Function tool: {functionTool.FunctionName}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(agentTools ??= []).Add(matchingTool!);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
(agentTools ??= []).Add(responseTool.AsAITool());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (requireInvocableTools && missingTools is { Count: > 0 })
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"The following prompt agent definition required tools were not provided: {string.Join(", ", missingTools)}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var agentOptions = new ChatClientAgentOptions()
|
||||||
|
{
|
||||||
|
Id = agentVersion.Id,
|
||||||
|
Name = agentVersion.Name,
|
||||||
|
Description = agentVersion.Description,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (agentDefinition is PromptAgentDefinition promptAgentDefinition)
|
||||||
|
{
|
||||||
|
agentOptions.ChatOptions ??= chatOptions?.Clone() ?? new();
|
||||||
|
agentOptions.Instructions = promptAgentDefinition.Instructions;
|
||||||
|
agentOptions.ChatOptions.Temperature = promptAgentDefinition.Temperature;
|
||||||
|
agentOptions.ChatOptions.TopP = promptAgentDefinition.TopP;
|
||||||
|
agentOptions.ChatOptions.Instructions = promptAgentDefinition.Instructions;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (agentTools is { Count: > 0 })
|
||||||
|
{
|
||||||
|
agentOptions.ChatOptions ??= chatOptions?.Clone() ?? new();
|
||||||
|
agentOptions.ChatOptions.Tools = agentTools;
|
||||||
|
}
|
||||||
|
|
||||||
|
return agentOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="ChatClientAgentOptions"/> configured for the specified agent version and
|
||||||
|
/// optional base options.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentVersion">The agent version to use when configuring the chat client agent options.</param>
|
||||||
|
/// <param name="options">An optional <see cref="ChatClientAgentOptions"/> instance whose relevant properties will be copied to the
|
||||||
|
/// returned options. If <see langword="null"/>, only default values are used.</param>
|
||||||
|
/// <param name="requireInvocableTools">Specifies whether the returned options must include invocable tools. Set to <see langword="true"/> to require
|
||||||
|
/// invocable tools; otherwise, <see langword="false"/>.</param>
|
||||||
|
/// <returns>A <see cref="ChatClientAgentOptions"/> instance configured according to the specified parameters.</returns>
|
||||||
|
private static ChatClientAgentOptions CreateChatClientAgentOptions(AgentVersion agentVersion, ChatClientAgentOptions? options, bool requireInvocableTools)
|
||||||
|
{
|
||||||
|
var agentOptions = CreateChatClientAgentOptions(agentVersion, options?.ChatOptions, requireInvocableTools);
|
||||||
|
if (options is not null)
|
||||||
|
{
|
||||||
|
agentOptions.AIContextProviderFactory = options.AIContextProviderFactory;
|
||||||
|
agentOptions.ChatMessageStoreFactory = options.ChatMessageStoreFactory;
|
||||||
|
agentOptions.UseProvidedChatClientAsIs = options.UseProvidedChatClientAsIs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return agentOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the specified AI tools to a prompt agent definition, while also ensuring that all invocable tools are provided.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentDefinition">The agent definition to which the tools will be applied. Must be a PromptAgentDefinition to support tools.</param>
|
||||||
|
/// <param name="tools">A list of AI tools to add to the agent definition. If null or empty, no tools are added.</param>
|
||||||
|
/// <exception cref="ArgumentException">Thrown if tools were provided but <paramref name="agentDefinition"/> is not a <see cref="PromptAgentDefinition"/>.</exception>
|
||||||
|
/// <exception cref="InvalidOperationException">When providing functions, they need to be invokable AIFunctions.</exception>
|
||||||
|
private static void ApplyToolsToAgentDefinition(AgentDefinition agentDefinition, IList<AITool>? tools)
|
||||||
|
{
|
||||||
|
if (tools is { Count: > 0 })
|
||||||
|
{
|
||||||
|
if (agentDefinition is not PromptAgentDefinition promptAgentDefinition)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Only prompt agent definitions support tools.", nameof(agentDefinition));
|
||||||
|
}
|
||||||
|
|
||||||
|
// When tools are provided, those should represent the complete set of tools for the agent definition.
|
||||||
|
// This is particularly important for existing agents so no duplication happens for what was already defined.
|
||||||
|
promptAgentDefinition.Tools.Clear();
|
||||||
|
|
||||||
|
foreach (var tool in tools)
|
||||||
|
{
|
||||||
|
// Ensure that any AIFunctions provided are In-Proc, not just the declarations.
|
||||||
|
if (tool is not AIFunction && (
|
||||||
|
tool.GetService<FunctionTool>() is not null // Declarative FunctionTool converted as AsAITool()
|
||||||
|
|| tool is AIFunctionDeclaration)) // AIFunctionDeclaration type
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("When providing functions, they need to be invokable AIFunctions. AIFunctions can be created correctly using AIFunctionFactory.Create");
|
||||||
|
}
|
||||||
|
|
||||||
|
promptAgentDefinition.Tools.Add(
|
||||||
|
// If this is a converted ResponseTool as AITool, we can directly retrieve the ResponseTool instance from GetService.
|
||||||
|
tool.GetService<ResponseTool>()
|
||||||
|
// Otherwise we should be able to convert existing MEAI Tool abstractions into OpenAI ResponseTools
|
||||||
|
?? tool.AsOpenAIResponseTool()
|
||||||
|
?? throw new InvalidOperationException("The provided AITool could not be converted to a ResponseTool, ensure that the AITool was created using responseTool.AsAITool() extension."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.ClientModel.Primitives;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
using Azure.AI.Agents;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
using OpenAI;
|
||||||
|
using OpenAI.Responses;
|
||||||
|
|
||||||
|
#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI.AzureAI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a chat client implementation that integrates with Azure AI Agents, enabling chat interactions using
|
||||||
|
/// Azure-specific agent capabilities.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class AzureAIAgentChatClient : DelegatingChatClient
|
||||||
|
{
|
||||||
|
private readonly ChatClientMetadata? _metadata;
|
||||||
|
private readonly AgentClient _agentClient;
|
||||||
|
private readonly AgentVersion? _agentVersion;
|
||||||
|
private readonly AgentRecord? _agentRecord;
|
||||||
|
private readonly ChatOptions? _chatOptions;
|
||||||
|
private readonly AgentReference _agentReference;
|
||||||
|
/// <summary>
|
||||||
|
/// The usage of a no-op model is a necessary change to avoid OpenAIClients to throw exceptions when
|
||||||
|
/// used with Azure AI Agents as the model used is now defined at the agent creation time.
|
||||||
|
/// </summary>
|
||||||
|
private const string NoOpModel = "no-op";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AzureAIAgentChatClient"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">An instance of <see cref="AgentClient"/> to interact with Azure AI Agents services.</param>
|
||||||
|
/// <param name="agentReference">An instance of <see cref="AgentReference"/> representing the specific agent to use.</param>
|
||||||
|
/// <param name="defaultModelId">The default model to use for the agent, if applicable.</param>
|
||||||
|
/// <param name="chatOptions">An instance of <see cref="ChatOptions"/> representing the options on how the agent was predefined.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// The <see cref="IChatClient"/> provided should be decorated with a <see cref="AzureAIAgentChatClient"/> for proper functionality.
|
||||||
|
/// </remarks>
|
||||||
|
internal AzureAIAgentChatClient(AgentClient agentClient, AgentReference agentReference, string? defaultModelId, ChatOptions? chatOptions, OpenAIClientOptions? openAIClientOptions = null)
|
||||||
|
: base(Throw.IfNull(agentClient)
|
||||||
|
.GetOpenAIClient(openAIClientOptions)
|
||||||
|
.GetOpenAIResponseClient(defaultModelId ?? NoOpModel)
|
||||||
|
.AsIChatClient())
|
||||||
|
{
|
||||||
|
this._agentClient = agentClient;
|
||||||
|
this._agentReference = Throw.IfNull(agentReference);
|
||||||
|
this._metadata = new ChatClientMetadata("azure.ai.agents", defaultModelId: defaultModelId);
|
||||||
|
this._chatOptions = chatOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AzureAIAgentChatClient"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">An instance of <see cref="AgentClient"/> to interact with Azure AI Agents services.</param>
|
||||||
|
/// <param name="agentRecord">An instance of <see cref="AgentRecord"/> representing the specific agent to use.</param>
|
||||||
|
/// <param name="chatOptions">An instance of <see cref="ChatOptions"/> representing the options on how the agent was predefined.</param>
|
||||||
|
/// <param name="openAIClientOptions">An optional <see cref="OpenAIClientOptions"/> for configuring the underlying OpenAI client.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// The <see cref="IChatClient"/> provided should be decorated with a <see cref="AzureAIAgentChatClient"/> for proper functionality.
|
||||||
|
/// </remarks>
|
||||||
|
internal AzureAIAgentChatClient(AgentClient agentClient, AgentRecord agentRecord, ChatOptions? chatOptions, OpenAIClientOptions? openAIClientOptions = null)
|
||||||
|
: this(agentClient, Throw.IfNull(agentRecord).Versions.Latest, chatOptions, openAIClientOptions)
|
||||||
|
{
|
||||||
|
this._agentRecord = agentRecord;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal AzureAIAgentChatClient(AgentClient agentClient, AgentVersion agentVersion, ChatOptions? chatOptions, OpenAIClientOptions? openAIClientOptions = null)
|
||||||
|
: this(
|
||||||
|
agentClient,
|
||||||
|
new AgentReference(Throw.IfNull(agentVersion).Name) { Version = agentVersion.Version },
|
||||||
|
(agentVersion.Definition as PromptAgentDefinition)?.Model,
|
||||||
|
chatOptions,
|
||||||
|
openAIClientOptions)
|
||||||
|
{
|
||||||
|
this._agentVersion = agentVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override object? GetService(Type serviceType, object? serviceKey = null)
|
||||||
|
{
|
||||||
|
return (serviceKey is null && serviceType == typeof(ChatClientMetadata))
|
||||||
|
? this._metadata
|
||||||
|
: (serviceKey is null && serviceType == typeof(AgentClient))
|
||||||
|
? this._agentClient
|
||||||
|
: (serviceKey is null && serviceType == typeof(AgentVersion))
|
||||||
|
? this._agentVersion
|
||||||
|
: (serviceKey is null && serviceType == typeof(AgentRecord))
|
||||||
|
? this._agentRecord
|
||||||
|
: (serviceKey is null && serviceType == typeof(AgentReference))
|
||||||
|
? this._agentReference
|
||||||
|
: base.GetService(serviceType, serviceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<ChatResponse> GetResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var agentOptions = this.GetAgentEnabledChatOptions(options);
|
||||||
|
|
||||||
|
return await base.GetResponseAsync(messages, agentOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async override IAsyncEnumerable<ChatResponseUpdate> GetStreamingResponseAsync(IEnumerable<ChatMessage> messages, ChatOptions? options = null, [EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var agentOptions = this.GetAgentEnabledChatOptions(options);
|
||||||
|
|
||||||
|
await foreach (var chunk in base.GetStreamingResponseAsync(messages, agentOptions, cancellationToken).ConfigureAwait(false))
|
||||||
|
{
|
||||||
|
yield return chunk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ChatOptions GetAgentEnabledChatOptions(ChatOptions? options)
|
||||||
|
{
|
||||||
|
// Start with a clone of the base chat options defined for the agent, if any.
|
||||||
|
ChatOptions agentEnabledChatOptions = this._chatOptions?.Clone() ?? new();
|
||||||
|
|
||||||
|
// Ignore per-request all options that can't be overridden.
|
||||||
|
agentEnabledChatOptions.Instructions = null;
|
||||||
|
agentEnabledChatOptions.Tools = null;
|
||||||
|
agentEnabledChatOptions.Temperature = null;
|
||||||
|
agentEnabledChatOptions.TopP = null;
|
||||||
|
agentEnabledChatOptions.PresencePenalty = null;
|
||||||
|
|
||||||
|
// Use the conversation from the request, or the one defined at the client level.
|
||||||
|
agentEnabledChatOptions.ConversationId = options?.ConversationId ?? this._chatOptions?.ConversationId;
|
||||||
|
|
||||||
|
// Preserve the original RawRepresentationFactory
|
||||||
|
var originalFactory = options?.RawRepresentationFactory;
|
||||||
|
|
||||||
|
agentEnabledChatOptions.RawRepresentationFactory = (client) =>
|
||||||
|
{
|
||||||
|
if (originalFactory?.Invoke(this) is not ResponseCreationOptions responseCreationOptions)
|
||||||
|
{
|
||||||
|
responseCreationOptions = new ResponseCreationOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.SetAgentReference(responseCreationOptions);
|
||||||
|
|
||||||
|
return responseCreationOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
return agentEnabledChatOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the SetAdditionalProperty/SetAgentReference/SetConversationReference extensions in Azure.AI.Agents does not yet support the recent updates in OpenAI 2.6.0
|
||||||
|
// The methods below are copied and adapted to the new OpenAI SDK 2.6.0 structure where the Patch property is now exposed directly on ResponseCreationOptions and
|
||||||
|
// may be removed once the Azure.AI.Agents package is updated to support OpenAI SDK 2.6+.
|
||||||
|
#pragma warning disable SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
|
private static void SetAdditionalProperty(ResponseCreationOptions responseCreationOptions, string key, BinaryData value)
|
||||||
|
{
|
||||||
|
responseCreationOptions.Patch.Set([.. "$."u8, .. Encoding.UTF8.GetBytes(key)], value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetAgentReference(ResponseCreationOptions responseCreationOptions)
|
||||||
|
{
|
||||||
|
SetAdditionalProperty(responseCreationOptions, "agent", ModelReaderWriter.Write(this._agentReference, new ModelReaderWriterOptions("W"), AzureAIAgentsContext.Default));
|
||||||
|
responseCreationOptions.Patch.Remove([.. "$."u8, .. Encoding.UTF8.GetBytes("model")]);
|
||||||
|
}
|
||||||
|
#pragma warning restore SCME0001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
|
||||||
|
}
|
||||||
+64
@@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="CodeInterpreterTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class CodeInterpreterToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="CodeInterpreterToolDefinition"/> from a <see cref="CodeInterpreterTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="CodeInterpreterTool"/></param>
|
||||||
|
internal static CodeInterpreterToolDefinition CreateCodeInterpreterToolDefinition(this CodeInterpreterTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
return new CodeInterpreterToolDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a <see cref="CodeInterpreterTool"/> to an <see cref="OpenAI.Responses.CodeInterpreterTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="CodeInterpreterTool"/></param>
|
||||||
|
/// <returns>A new <see cref="OpenAI.Responses.CodeInterpreterTool"/> instance configured with the container ID from the tool's extension data.</returns>
|
||||||
|
internal static OpenAI.Responses.CodeInterpreterTool CreateCodeInterpreterTool(this CodeInterpreterTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
var containerId = tool.ExtensionData?.GetPropertyOrNull<StringDataValue>(InitializablePropertyPath.Create("containerId"))?.Value;
|
||||||
|
Throw.IfNull(containerId, "The 'containerId' property must be specified in the CodeInterpreterTool's extension data to create a code interpreter tool.");
|
||||||
|
|
||||||
|
return new OpenAI.Responses.CodeInterpreterTool(new OpenAI.Responses.CodeInterpreterToolContainer(containerId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collects the file IDs from the extension data of a <see cref="CodeInterpreterTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="CodeInterpreterTool"/></param>
|
||||||
|
internal static List<string>? GetFileIds(this CodeInterpreterTool tool)
|
||||||
|
{
|
||||||
|
var fileIds = tool.ExtensionData?.GetPropertyOrNull<TableDataValue>(InitializablePropertyPath.Create("fileIds"));
|
||||||
|
return fileIds is not null
|
||||||
|
? [.. fileIds.Values.Select(fileId => fileId.GetPropertyOrNull<StringDataValue>(InitializablePropertyPath.Create("value"))?.Value)]
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collects the data sources from the extension data of a <see cref="CodeInterpreterTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="CodeInterpreterTool"/></param>
|
||||||
|
internal static List<VectorStoreDataSource>? GetDataSources(this CodeInterpreterTool tool)
|
||||||
|
{
|
||||||
|
var dataSources = tool.ExtensionData?.GetPropertyOrNull<TableDataValue>(InitializablePropertyPath.Create("dataSources"));
|
||||||
|
return dataSources is not null
|
||||||
|
? dataSources.Values.Select(dataSource => dataSource.CreateDataSource()).ToList()
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="FileSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class FileSearchToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="FileSearchToolDefinition"/> from a <see cref="FileSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="FileSearchTool"/></param>
|
||||||
|
internal static FileSearchToolDefinition CreateFileSearchToolDefinition(this FileSearchTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
// TODO: Add support for FileSearchToolDefinitionDetails.
|
||||||
|
|
||||||
|
return new FileSearchToolDefinition();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an <see cref="OpenAI.Responses.FileSearchTool"/> from a <see cref="FileSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="FileSearchTool"/></param>
|
||||||
|
/// <returns>A new <see cref="OpenAI.Responses.FileSearchTool"/> instance configured with the vector store IDs.</returns>
|
||||||
|
internal static OpenAI.Responses.FileSearchTool CreateFileSearchTool(this FileSearchTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
return new OpenAI.Responses.FileSearchTool(tool.GetVectorStoreIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the vector store IDs for the specified <see cref="FileSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="FileSearchTool"/></param>
|
||||||
|
internal static List<string>? GetVectorStoreIds(this FileSearchTool tool)
|
||||||
|
{
|
||||||
|
return tool.VectorStoreIds?.LiteralValue.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static IList<VectorStoreConfigurations>? GetVectorStoreConfigurations(this FileSearchTool tool)
|
||||||
|
{
|
||||||
|
var dataSources = tool.ExtensionData?.GetPropertyOrNull<TableDataValue>(InitializablePropertyPath.Create("options.configurations"));
|
||||||
|
return dataSources?.Values.Select(value => value.CreateVectorStoreConfiguration()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static VectorStoreConfigurations CreateVectorStoreConfiguration(this RecordDataValue value)
|
||||||
|
{
|
||||||
|
Throw.IfNull(value);
|
||||||
|
|
||||||
|
var storeName = value.GetPropertyOrNull<StringDataValue>(InitializablePropertyPath.Create("storeName"))?.Value;
|
||||||
|
Throw.IfNullOrEmpty(storeName);
|
||||||
|
|
||||||
|
var dataSources = value.GetDataSources();
|
||||||
|
Throw.IfNull(dataSources);
|
||||||
|
|
||||||
|
return new VectorStoreConfigurations(storeName, new VectorStoreConfiguration(dataSources));
|
||||||
|
}
|
||||||
|
}
|
||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
using OpenAI.Responses;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="InvokeClientTaskAction"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class FunctionToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="FunctionToolDefinition"/> from a <see cref="InvokeClientTaskAction"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="InvokeClientTaskAction"/></param>
|
||||||
|
internal static FunctionToolDefinition CreateFunctionToolDefinition(this InvokeClientTaskAction tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
Throw.IfNull(tool.Name);
|
||||||
|
|
||||||
|
BinaryData parameters = tool.GetParameters();
|
||||||
|
|
||||||
|
return new FunctionToolDefinition(
|
||||||
|
name: tool.Name,
|
||||||
|
description: tool.Description,
|
||||||
|
parameters: parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="FunctionTool"/> from a <see cref="InvokeClientTaskAction"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="InvokeClientTaskAction"/></param>
|
||||||
|
/// <returns>A new <see cref="FunctionTool"/> instance configured with the function name, parameters, and description.</returns>
|
||||||
|
internal static FunctionTool CreateFunctionTool(this InvokeClientTaskAction tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
Throw.IfNull(tool.Name);
|
||||||
|
|
||||||
|
BinaryData parameters = tool.GetParameters();
|
||||||
|
|
||||||
|
return new FunctionTool(
|
||||||
|
functionName: tool.Name,
|
||||||
|
functionParameters: parameters,
|
||||||
|
strictModeEnabled: null)
|
||||||
|
{
|
||||||
|
FunctionDescription = tool.Description
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the parameters schema for a <see cref="InvokeClientTaskAction"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="InvokeClientTaskAction"/></param>
|
||||||
|
internal static BinaryData GetParameters(this InvokeClientTaskAction tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
var parameters = tool.ClientActionInputSchema?.GetSchema().ToString() ?? DefaultSchema;
|
||||||
|
|
||||||
|
return new BinaryData(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
private const string DefaultSchema = "{\"type\":\"object\",\"properties\":{},\"additionalProperties\":false}";
|
||||||
|
}
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="HostedCodeInterpreterTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class HostedCodeInterpreterToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="CodeInterpreterToolDefinition"/> from a <see cref="HostedCodeInterpreterTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="CodeInterpreterTool"/></param>
|
||||||
|
internal static CodeInterpreterToolDefinition CreateHostedCodeInterpreterToolDefinition(this HostedCodeInterpreterTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
return new CodeInterpreterToolDefinition();
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="HostedFileSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class HostedFileSearchToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="FileSearchToolDefinition"/> from a <see cref="HostedFileSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="HostedFileSearchTool"/></param>
|
||||||
|
internal static FileSearchToolDefinition CreateFileSearchToolDefinition(this HostedFileSearchTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
// TODO: Add support for FileSearchToolDefinitionDetails.
|
||||||
|
|
||||||
|
return new FileSearchToolDefinition();
|
||||||
|
}
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="HostedMcpServerTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class HostedMcpServerToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="MCPToolDefinition"/> from a <see cref="HostedMcpServerTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="HostedMcpServerTool"/></param>
|
||||||
|
internal static MCPToolDefinition CreateMcpToolDefinition(this HostedMcpServerTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
Throw.IfNull(tool.ServerName);
|
||||||
|
Throw.IfNull(tool.ServerAddress);
|
||||||
|
|
||||||
|
var definition = new MCPToolDefinition(tool.ServerName, tool.ServerAddress);
|
||||||
|
tool.AllowedTools?.ToList().ForEach(definition.AllowedTools.Add);
|
||||||
|
return definition;
|
||||||
|
}
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="HostedWebSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class HostedWebSearchToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="BingGroundingToolDefinition"/> from a <see cref="HostedWebSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="HostedWebSearchTool"/></param>
|
||||||
|
internal static BingGroundingToolDefinition CreateBingGroundingToolDefinition(this HostedWebSearchTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
// TODO: Add support for BingGroundingSearchToolParameters.
|
||||||
|
var parameters = new BingGroundingSearchToolParameters([]);
|
||||||
|
|
||||||
|
return new BingGroundingToolDefinition(parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
using OpenAI.Responses;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="McpServerTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class McpServerToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="MCPToolDefinition"/> from a <see cref="McpServerTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="McpServerTool"/></param>
|
||||||
|
internal static MCPToolDefinition CreateMcpToolDefinition(this McpServerTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
Throw.IfNull(tool.ServerName?.LiteralValue);
|
||||||
|
Throw.IfNull(tool.Connection);
|
||||||
|
|
||||||
|
// TODO: Add support for additional properties
|
||||||
|
|
||||||
|
var connection = tool.Connection as AnonymousConnection ?? throw new ArgumentException($"Only AnonymousConnection is supported for MCP Server Tool connections. Actual connection type: {tool.Connection.GetType().Name}", nameof(tool));
|
||||||
|
var serverUrl = connection.Endpoint?.LiteralValue;
|
||||||
|
Throw.IfNullOrEmpty(serverUrl, nameof(connection.Endpoint));
|
||||||
|
|
||||||
|
return new MCPToolDefinition(tool.ServerName?.LiteralValue, serverUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="McpTool"/> from a <see cref="McpServerTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="McpServerTool"/></param>
|
||||||
|
/// <returns>A new <see cref="McpTool"/> instance configured with the server name and URL.</returns>
|
||||||
|
internal static McpTool CreateMcpTool(this McpServerTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
Throw.IfNull(tool.ServerName?.LiteralValue);
|
||||||
|
Throw.IfNull(tool.Connection);
|
||||||
|
|
||||||
|
// TODO: Add support for headers
|
||||||
|
|
||||||
|
var connection = tool.Connection as AnonymousConnection ?? throw new ArgumentException("Only AnonymousConnection is supported for MCP Server Tool connections.", nameof(tool));
|
||||||
|
var serverUrl = connection.Endpoint?.LiteralValue;
|
||||||
|
Throw.IfNullOrEmpty(serverUrl, nameof(connection.Endpoint));
|
||||||
|
|
||||||
|
return new McpTool(tool.ServerName?.LiteralValue, serverUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
+142
@@ -0,0 +1,142 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
using OpenAI.Responses;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="GptComponentMetadata"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class PromptAgentExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Return the Foundry tool definitions which corresponds with the provided <see cref="GptComponentMetadata"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="promptAgent">Instance of <see cref="GptComponentMetadata"/></param>
|
||||||
|
internal static IEnumerable<Azure.AI.Agents.Persistent.ToolDefinition> GetToolDefinitions(this GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
return promptAgent.Tools.Select<TaskAction, Azure.AI.Agents.Persistent.ToolDefinition>(tool =>
|
||||||
|
{
|
||||||
|
return tool switch
|
||||||
|
{
|
||||||
|
CodeInterpreterTool codeInterpreterTool => codeInterpreterTool.CreateCodeInterpreterToolDefinition(),
|
||||||
|
InvokeClientTaskAction functionTool => functionTool.CreateFunctionToolDefinition(),
|
||||||
|
FileSearchTool fileSearchTool => fileSearchTool.CreateFileSearchToolDefinition(),
|
||||||
|
WebSearchTool webSearchTool => webSearchTool.CreateBingGroundingToolDefinition(),
|
||||||
|
McpServerTool mcpServerTool => mcpServerTool.CreateMcpToolDefinition(),
|
||||||
|
// TODO: Add other tool types as custom tools
|
||||||
|
// AzureAISearch
|
||||||
|
// AzureFunction
|
||||||
|
// OpenApi
|
||||||
|
_ => throw new NotSupportedException($"Unable to create tool definition because of unsupported tool type: {tool.Kind}"),
|
||||||
|
};
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return the Foundry tool resources which corresponds with the provided <see cref="GptComponentMetadata"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="promptAgent">Instance of <see cref="GptComponentMetadata"/></param>
|
||||||
|
internal static ToolResources GetToolResources(this GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
var toolResources = new ToolResources();
|
||||||
|
|
||||||
|
var codeInterpreter = promptAgent.GetCodeInterpreterToolResource();
|
||||||
|
if (codeInterpreter is not null)
|
||||||
|
{
|
||||||
|
toolResources.CodeInterpreter = codeInterpreter;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileSearch = promptAgent.GetFileSearchToolResource();
|
||||||
|
if (fileSearch is not null)
|
||||||
|
{
|
||||||
|
toolResources.FileSearch = fileSearch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Handle MCP tool resources
|
||||||
|
|
||||||
|
return toolResources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the Foundry response tools which correspond with the provided <see cref="GptComponentMetadata"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="promptAgent">Instance of <see cref="GptComponentMetadata"/>.</param>
|
||||||
|
/// <returns>A collection of <see cref="ResponseTool"/> instances corresponding to the tools defined in the agent.</returns>
|
||||||
|
internal static IEnumerable<ResponseTool> GetResponseTools(this GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
return promptAgent.Tools.Select<TaskAction, ResponseTool>(tool =>
|
||||||
|
{
|
||||||
|
return tool switch
|
||||||
|
{
|
||||||
|
CodeInterpreterTool codeInterpreterTool => codeInterpreterTool.CreateCodeInterpreterTool(),
|
||||||
|
InvokeClientTaskAction functionTool => functionTool.CreateFunctionTool(),
|
||||||
|
FileSearchTool fileSearchTool => fileSearchTool.CreateFileSearchTool(),
|
||||||
|
WebSearchTool webSearchTool => webSearchTool.CreateWebSearchTool(),
|
||||||
|
McpServerTool mcpServerTool => mcpServerTool.CreateMcpTool(),
|
||||||
|
// TODO: Add other tool types as custom tools
|
||||||
|
// AzureAISearch
|
||||||
|
// AzureFunction
|
||||||
|
// OpenApi
|
||||||
|
_ => throw new NotSupportedException($"Unable to create response tool because of unsupported tool type: {tool.Kind}"),
|
||||||
|
};
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region private
|
||||||
|
private static CodeInterpreterToolResource? GetCodeInterpreterToolResource(this GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
CodeInterpreterToolResource? resource = null;
|
||||||
|
|
||||||
|
var codeInterpreter = (CodeInterpreterTool?)promptAgent.GetFirstAgentTool<CodeInterpreterTool>();
|
||||||
|
if (codeInterpreter is not null)
|
||||||
|
{
|
||||||
|
var fileIds = codeInterpreter.GetFileIds();
|
||||||
|
var dataSources = codeInterpreter.GetDataSources();
|
||||||
|
if (fileIds is not null || dataSources is not null)
|
||||||
|
{
|
||||||
|
resource = new CodeInterpreterToolResource();
|
||||||
|
fileIds?.ForEach(id => resource.FileIds.Add(id));
|
||||||
|
dataSources?.ForEach(ds => resource.DataSources.Add(ds));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FileSearchToolResource? GetFileSearchToolResource(this GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
var fileSearch = (FileSearchTool?)promptAgent.GetFirstAgentTool<FileSearchTool>();
|
||||||
|
if (fileSearch is not null)
|
||||||
|
{
|
||||||
|
var vectorStoreIds = fileSearch.GetVectorStoreIds();
|
||||||
|
var vectorStores = fileSearch.GetVectorStoreConfigurations();
|
||||||
|
if (vectorStoreIds is not null || vectorStores is not null)
|
||||||
|
{
|
||||||
|
return new FileSearchToolResource(vectorStoreIds, vectorStores);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TaskAction? GetFirstAgentTool<T>(this GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
return promptAgent.Tools.FirstOrDefault(tool => tool is T);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="RecordDataType"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class RecordDataTypeExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="ChatResponseFormat"/> from a <see cref="RecordDataType"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recordDataType">Instance of <see cref="RecordDataType"/></param>
|
||||||
|
#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
|
||||||
|
#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
|
||||||
|
internal static BinaryData? AsBinaryData(this RecordDataType recordDataType)
|
||||||
|
{
|
||||||
|
Throw.IfNull(recordDataType);
|
||||||
|
|
||||||
|
if (recordDataType.Properties.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BinaryData.FromObjectAsJson(
|
||||||
|
new
|
||||||
|
{
|
||||||
|
type = "json_schema",
|
||||||
|
schema =
|
||||||
|
new
|
||||||
|
{
|
||||||
|
type = "object",
|
||||||
|
properties = recordDataType.Properties.AsObjectDictionary(),
|
||||||
|
additionalProperties = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
|
||||||
|
#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="RecordDataValue"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class RecordDataValueExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the data sources from the specified <see cref="RecordDataValue"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static List<VectorStoreDataSource>? GetDataSources(this RecordDataValue value)
|
||||||
|
{
|
||||||
|
var dataSources = value.GetPropertyOrNull<TableDataValue>(InitializablePropertyPath.Create("options.data_sources"));
|
||||||
|
return dataSources?.Values.Select(dataSource => dataSource.CreateDataSource()).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="VectorStoreDataSource"/> using the specified <see cref="RecordDataValue"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static VectorStoreDataSource CreateDataSource(this RecordDataValue value)
|
||||||
|
{
|
||||||
|
Throw.IfNull(value);
|
||||||
|
|
||||||
|
string? assetIdentifier = value.GetPropertyOrNull<StringDataValue>(InitializablePropertyPath.Create("assetIdentifier"))?.Value;
|
||||||
|
Throw.IfNullOrEmpty(assetIdentifier);
|
||||||
|
|
||||||
|
string? assetType = value.GetPropertyOrNull<StringDataValue>(InitializablePropertyPath.Create("assetType"))?.Value;
|
||||||
|
Throw.IfNullOrEmpty(assetType);
|
||||||
|
|
||||||
|
return new VectorStoreDataSource(assetIdentifier, new VectorStoreDataSourceAssetType(assetType));
|
||||||
|
}
|
||||||
|
}
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="WebSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class WebSearchToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="BingGroundingToolDefinition"/> from a <see cref="WebSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="WebSearchTool"/></param>
|
||||||
|
internal static BingGroundingToolDefinition CreateBingGroundingToolDefinition(this WebSearchTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
// TODO: Add support for BingGroundingSearchToolParameters.
|
||||||
|
var parameters = new BingGroundingSearchToolParameters([]);
|
||||||
|
|
||||||
|
return new BingGroundingToolDefinition(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="OpenAI.Responses.WebSearchTool"/> from a <see cref="WebSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="WebSearchTool"/></param>
|
||||||
|
/// <returns>A new <see cref="OpenAI.Responses.WebSearchTool"/> instance.</returns>
|
||||||
|
internal static OpenAI.Responses.WebSearchTool CreateWebSearchTool(this WebSearchTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
return new OpenAI.Responses.WebSearchTool();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Azure.AI.Agents;
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Azure.Core;
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides an <see cref="AgentFactory"/> which creates instances of <see cref="AIAgent"/> using a <see cref="AgentClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class FoundryAgentFactory : AgentFactory
|
||||||
|
{
|
||||||
|
private readonly AgentClient? _agentClient;
|
||||||
|
private readonly TokenCredential? _tokenCredential;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="FoundryAgentFactory"/> class with an associated <see cref="AgentClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The <see cref="AgentClient"/> instance to use for creating agents.</param>
|
||||||
|
/// <param name="configuration">The <see cref="IConfiguration"/> instance to use for configuration.</param>
|
||||||
|
public FoundryAgentFactory(AgentClient agentClient, IConfiguration? configuration = null) : base(configuration)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
|
||||||
|
this._agentClient = agentClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="FoundryAgentFactory"/> class with an associated <see cref="TokenCredential"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tokenCredential">The <see cref="TokenCredential"/> to use for authenticating requests.</param>
|
||||||
|
/// <param name="configuration">The <see cref="IConfiguration"/> instance to use for configuration.</param>
|
||||||
|
public FoundryAgentFactory(TokenCredential tokenCredential, IConfiguration? configuration = null) : base(configuration)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tokenCredential);
|
||||||
|
|
||||||
|
this._tokenCredential = tokenCredential;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<AIAgent?> TryCreateAsync(GptComponentMetadata promptAgent, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
var agentClient = this._agentClient ?? this.CreateAgentClient(promptAgent);
|
||||||
|
|
||||||
|
var modelId = promptAgent.Model?.ModelNameHint;
|
||||||
|
if (string.IsNullOrEmpty(modelId))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The model id must be specified in the agent definition model to create a foundry agent.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var modelOptions = promptAgent.Model?.Options;
|
||||||
|
|
||||||
|
var promptAgentDefinition = new PromptAgentDefinition(model: modelId)
|
||||||
|
{
|
||||||
|
Instructions = promptAgent.Instructions?.ToTemplateString(),
|
||||||
|
Temperature = (float?)modelOptions?.Temperature?.LiteralValue,
|
||||||
|
TopP = (float?)modelOptions?.TopP?.LiteralValue,
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var tool in promptAgent.GetResponseTools())
|
||||||
|
{
|
||||||
|
promptAgentDefinition.Tools.Add(tool);
|
||||||
|
}
|
||||||
|
|
||||||
|
var agentVersionCreationOptions = new AgentVersionCreationOptions(promptAgentDefinition);
|
||||||
|
|
||||||
|
var metadata = promptAgent.Metadata?.ToDictionary();
|
||||||
|
if (metadata is not null)
|
||||||
|
{
|
||||||
|
foreach (var kvp in metadata)
|
||||||
|
{
|
||||||
|
agentVersionCreationOptions.Metadata.Add(kvp.Key, kvp.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var agentVersion = await agentClient.CreateAgentVersionAsync(agentName: promptAgent.Name, options: agentVersionCreationOptions, cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
return agentClient.GetAIAgent(agentVersion, cancellationToken: cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AgentClient CreateAgentClient(GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
var externalModel = promptAgent.Model as CurrentModels;
|
||||||
|
var connection = externalModel?.Connection as RemoteConnection;
|
||||||
|
if (connection is not null)
|
||||||
|
{
|
||||||
|
var endpoint = connection.Endpoint?.Eval(this.Engine);
|
||||||
|
if (string.IsNullOrEmpty(endpoint))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The endpoint must be specified in the agent definition model connection to create an AgentClient.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._tokenCredential is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("A TokenCredential must be registered in the service provider to create an AgentClient.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AgentClient(new Uri(endpoint), this._tokenCredential);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("An AgentClient must be registered in the service provider or a RemoteConnection must be specified in the agent definition model connection to create an AgentClient.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Azure.Core;
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides an <see cref="AgentFactory"/> which creates instances of <see cref="AIAgent"/> using a <see cref="PersistentAgentsClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class FoundryPersistentAgentFactory : AgentFactory
|
||||||
|
{
|
||||||
|
private readonly PersistentAgentsClient? _agentClient;
|
||||||
|
private readonly TokenCredential? _tokenCredential;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="FoundryPersistentAgentFactory"/> class with an associated <see cref="PersistentAgentsClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentClient">The <see cref="PersistentAgentsClient"/> instance to use for creating agents.</param>
|
||||||
|
/// <param name="configuration">The <see cref="IConfiguration"/> instance to use for configuration.</param>
|
||||||
|
public FoundryPersistentAgentFactory(PersistentAgentsClient agentClient, IConfiguration? configuration = null) : base(configuration)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentClient);
|
||||||
|
|
||||||
|
this._agentClient = agentClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="FoundryPersistentAgentFactory"/> class with an associated <see cref="TokenCredential"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tokenCredential">The <see cref="TokenCredential"/> to use for authenticating requests.</param>
|
||||||
|
/// <param name="configuration">The <see cref="IConfiguration"/> instance to use for configuration.</param>
|
||||||
|
public FoundryPersistentAgentFactory(TokenCredential tokenCredential, IConfiguration? configuration = null) : base(configuration)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tokenCredential);
|
||||||
|
|
||||||
|
this._tokenCredential = tokenCredential;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<AIAgent?> TryCreateAsync(GptComponentMetadata promptAgent, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
var agentClient = this._agentClient ?? this.CreatePersistentAgentClient(promptAgent);
|
||||||
|
|
||||||
|
var modelId = promptAgent.Model?.ModelNameHint;
|
||||||
|
if (string.IsNullOrEmpty(modelId))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The model id must be specified in the agent definition model to create a foundry agent.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//var outputSchema = promptAgent.OutputType; TODO: Fix converting RecordDataType to BinaryData
|
||||||
|
var modelOptions = promptAgent.Model?.Options;
|
||||||
|
|
||||||
|
return await agentClient.CreateAIAgentAsync(
|
||||||
|
model: modelId,
|
||||||
|
name: promptAgent.Name,
|
||||||
|
instructions: promptAgent.Instructions?.ToTemplateString(),
|
||||||
|
tools: promptAgent.GetToolDefinitions(),
|
||||||
|
toolResources: promptAgent.GetToolResources(),
|
||||||
|
temperature: (float?)modelOptions?.Temperature?.LiteralValue,
|
||||||
|
topP: (float?)modelOptions?.TopP?.LiteralValue,
|
||||||
|
//responseFormat: outputSchema.AsBinaryData(), TODO: Fix converting RecordDataType to BinaryData
|
||||||
|
metadata: promptAgent.Metadata?.ToDictionary(),
|
||||||
|
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private PersistentAgentsClient CreatePersistentAgentClient(GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
var externalModel = promptAgent.Model as CurrentModels;
|
||||||
|
var connection = externalModel?.Connection as RemoteConnection;
|
||||||
|
if (connection is not null)
|
||||||
|
{
|
||||||
|
var endpoint = connection.Endpoint?.Eval(this.Engine);
|
||||||
|
if (string.IsNullOrEmpty(endpoint))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The endpoint must be specified in the agent definition model connection to create an PersistentAgentsClient.");
|
||||||
|
}
|
||||||
|
if (this._tokenCredential is null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("A TokenCredential must be registered in the service provider to create an PersistentAgentsClient.");
|
||||||
|
}
|
||||||
|
return new PersistentAgentsClient(endpoint, this._tokenCredential);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("A PersistentAgentsClient must be registered in the service provider or a FoundryConnection must be specified in the agent definition model connection to create an PersistentAgentsClient.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI.Declarative.AzureAI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A class to describe the parameters of an <see cref="AIFunction"/> in a JSON Schema friendly way.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class JsonSchemaFunctionParameters
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The type of schema which is always "object" when describing function parameters.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("type")]
|
||||||
|
public string Type => "object";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of required properties.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("required")]
|
||||||
|
public List<string> Required { get; set; } = [];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dictionary of properties, keyed by name => JSON Schema.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("properties")]
|
||||||
|
public Dictionary<string, JsonElement> Properties { get; set; } = [];
|
||||||
|
}
|
||||||
+52
@@ -0,0 +1,52 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
|
||||||
|
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
|
||||||
|
<VersionSuffix>preview</VersionSuffix>
|
||||||
|
<NoWarn>$(NoWarn);MEAI001;OPENAI001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<InjectSharedThrow>true</InjectSharedThrow>
|
||||||
|
<InjectDiagnosticClassesOnLegacy>true</InjectDiagnosticClassesOnLegacy>
|
||||||
|
<InjectTrimAttributesOnLegacy>true</InjectTrimAttributesOnLegacy>
|
||||||
|
<InjectIsExternalInitOnLegacy>true</InjectIsExternalInitOnLegacy>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- NuGet Package Settings -->
|
||||||
|
<Title>Microsoft Agent Framework Declarative AzureAI</Title>
|
||||||
|
<Description>Provides Microsoft Agent Framework support for declarative AzureAI agents.</Description>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Azure.Identity" />
|
||||||
|
<PackageReference Include="Azure.AI.Agents" />
|
||||||
|
<PackageReference Include="Azure.AI.OpenAI" />
|
||||||
|
<PackageReference Include="System.Diagnostics.DiagnosticSource" />
|
||||||
|
<PackageReference Include="Azure.AI.Agents.Persistent" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.AI" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" />
|
||||||
|
<PackageReference Include="Microsoft.Bot.ObjectModel" />
|
||||||
|
<PackageReference Include="Microsoft.Bot.ObjectModel.Json" />
|
||||||
|
<PackageReference Include="Microsoft.Bot.ObjectModel.PowerFx" />
|
||||||
|
<PackageReference Include="Microsoft.PowerFx.Interpreter" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Microsoft.Agents.AI.Abstractions\Microsoft.Agents.AI.Abstractions.csproj" />
|
||||||
|
<ProjectReference Include="..\Microsoft.Agents.AI.AzureAI.Persistent\Microsoft.Agents.AI.AzureAI.Persistent.csproj" />
|
||||||
|
<ProjectReference Include="..\Microsoft.Agents.AI.AzureAI\Microsoft.Agents.AI.AzureAI.csproj" />
|
||||||
|
<ProjectReference Include="..\Microsoft.Agents.AI.Declarative\Microsoft.Agents.AI.Declarative.csproj" />
|
||||||
|
<ProjectReference Include="..\Microsoft.Agents.AI.OpenAI\Microsoft.Agents.AI.OpenAI.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<InternalsVisibleTo Include="Microsoft.Agents.AI.Declarative.AzureAI.UnitTests" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
using System;
|
||||||
|
using System.ClientModel;
|
||||||
|
using Azure.AI.OpenAI;
|
||||||
|
using Azure.Core;
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
using OpenAI;
|
||||||
|
using OpenAI.Assistants;
|
||||||
|
using OpenAI.Chat;
|
||||||
|
using OpenAI.Responses;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides an <see cref="OpenAIAgentFactory"/> abstract base class.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class OpenAIAgentFactory : AgentFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenAIAgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
protected OpenAIAgentFactory(IConfiguration? configuration, ILoggerFactory? loggerFactory) : base(configuration)
|
||||||
|
{
|
||||||
|
this.LoggerFactory = loggerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenAIAgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
protected OpenAIAgentFactory(Uri endpoint, TokenCredential tokenCredential, IConfiguration? configuration, ILoggerFactory? loggerFactory) : base(configuration)
|
||||||
|
{
|
||||||
|
Throw.IfNull(endpoint);
|
||||||
|
Throw.IfNull(tokenCredential);
|
||||||
|
|
||||||
|
this._endpoint = endpoint;
|
||||||
|
this._tokenCredential = tokenCredential;
|
||||||
|
this.LoggerFactory = loggerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="ILoggerFactory"/> instance used for creating loggers.
|
||||||
|
/// </summary>
|
||||||
|
protected ILoggerFactory? LoggerFactory { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="ChatClient"/> class.
|
||||||
|
/// </summary>
|
||||||
|
protected ChatClient? CreateChatClient(GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
var model = promptAgent.Model as CurrentModels;
|
||||||
|
var provider = model?.Provider?.Value ?? ModelProvider.OpenAI;
|
||||||
|
if (provider == ModelProvider.OpenAI)
|
||||||
|
{
|
||||||
|
return this.CreateOpenAIChatClient(promptAgent);
|
||||||
|
}
|
||||||
|
else if (provider == ModelProvider.AzureOpenAI)
|
||||||
|
{
|
||||||
|
Throw.IfNull(this._endpoint, "A endpoint must be specified to create an Azure OpenAI client");
|
||||||
|
Throw.IfNull(this._tokenCredential, "A token credential must be specified to create an Azure OpenAI client");
|
||||||
|
return CreateAzureOpenAIChatClient(promptAgent, this._endpoint, this._tokenCredential);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="AssistantClient"/> class.
|
||||||
|
/// </summary>
|
||||||
|
protected AssistantClient? CreateAssistantClient(GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
var model = promptAgent.Model as CurrentModels;
|
||||||
|
var provider = model?.Provider?.Value ?? ModelProvider.OpenAI;
|
||||||
|
if (provider == ModelProvider.OpenAI)
|
||||||
|
{
|
||||||
|
return this.CreateOpenAIAssistantClient(promptAgent);
|
||||||
|
}
|
||||||
|
else if (provider == ModelProvider.AzureOpenAI)
|
||||||
|
{
|
||||||
|
Throw.IfNull(this._endpoint, "The connection endpoint must be specified to create an Azure OpenAI client.");
|
||||||
|
Throw.IfNull(this._tokenCredential, "A token credential must be specified to create an Azure OpenAI client");
|
||||||
|
return CreateAzureOpenAIAssistantClient(promptAgent, this._endpoint, this._tokenCredential);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenAIResponseClient"/> class.
|
||||||
|
/// </summary>
|
||||||
|
protected OpenAIResponseClient? CreateResponseClient(GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
var model = promptAgent.Model as CurrentModels;
|
||||||
|
var provider = model?.Provider?.Value ?? ModelProvider.OpenAI;
|
||||||
|
if (provider == ModelProvider.OpenAI)
|
||||||
|
{
|
||||||
|
return this.CreateOpenAIResponseClient(promptAgent);
|
||||||
|
}
|
||||||
|
else if (provider == ModelProvider.AzureOpenAI)
|
||||||
|
{
|
||||||
|
Throw.IfNull(this._endpoint, "The connection endpoint must be specified to create an Azure OpenAI client.");
|
||||||
|
Throw.IfNull(this._tokenCredential, "A token credential must be specified to create an Azure OpenAI client");
|
||||||
|
return CreateAzureOpenAIResponseClient(promptAgent, this._endpoint, this._tokenCredential);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region private
|
||||||
|
private readonly Uri? _endpoint;
|
||||||
|
private readonly TokenCredential? _tokenCredential;
|
||||||
|
|
||||||
|
private ChatClient CreateOpenAIChatClient(GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
var modelId = promptAgent.Model?.ModelNameHint;
|
||||||
|
Throw.IfNullOrEmpty(modelId, "The model id must be specified in the agent definition to create an OpenAI agent.");
|
||||||
|
|
||||||
|
return this.CreateOpenAIClient(promptAgent).GetChatClient(modelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ChatClient CreateAzureOpenAIChatClient(GptComponentMetadata promptAgent, Uri endpoint, TokenCredential tokenCredential)
|
||||||
|
{
|
||||||
|
var deploymentName = promptAgent.Model?.ModelNameHint;
|
||||||
|
Throw.IfNullOrEmpty(deploymentName, "The deployment name (using model.id) must be specified in the agent definition to create an Azure OpenAI agent.");
|
||||||
|
|
||||||
|
return new AzureOpenAIClient(endpoint, tokenCredential).GetChatClient(deploymentName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AssistantClient CreateOpenAIAssistantClient(GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
var modelId = promptAgent.Model?.ModelNameHint;
|
||||||
|
Throw.IfNullOrEmpty(modelId, "The model id must be specified in the agent definition to create an OpenAI agent.");
|
||||||
|
|
||||||
|
return this.CreateOpenAIClient(promptAgent).GetAssistantClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static AssistantClient CreateAzureOpenAIAssistantClient(GptComponentMetadata promptAgent, Uri endpoint, TokenCredential tokenCredential)
|
||||||
|
{
|
||||||
|
var deploymentName = promptAgent.Model?.ModelNameHint;
|
||||||
|
Throw.IfNullOrEmpty(deploymentName, "The deployment name (using model.id) must be specified in the agent definition to create an Azure OpenAI agent.");
|
||||||
|
|
||||||
|
return new AzureOpenAIClient(endpoint, tokenCredential).GetAssistantClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
private OpenAIResponseClient CreateOpenAIResponseClient(GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
var modelId = promptAgent.Model?.ModelNameHint;
|
||||||
|
Throw.IfNullOrEmpty(modelId, "The model id must be specified in the agent definition to create an OpenAI agent.");
|
||||||
|
|
||||||
|
return this.CreateOpenAIClient(promptAgent).GetOpenAIResponseClient(modelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static OpenAIResponseClient CreateAzureOpenAIResponseClient(GptComponentMetadata promptAgent, Uri endpoint, TokenCredential tokenCredential)
|
||||||
|
{
|
||||||
|
var deploymentName = promptAgent.Model?.ModelNameHint;
|
||||||
|
Throw.IfNullOrEmpty(deploymentName, "The deployment name (using model.id) must be specified in the agent definition to create an Azure OpenAI agent.");
|
||||||
|
|
||||||
|
return new AzureOpenAIClient(endpoint, tokenCredential).GetOpenAIResponseClient(deploymentName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OpenAIClient CreateOpenAIClient(GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
var model = promptAgent.Model as CurrentModels;
|
||||||
|
|
||||||
|
var keyConnection = model?.Connection as ApiKeyConnection;
|
||||||
|
Throw.IfNull(keyConnection, "A key connection must be specified when create an OpenAI client");
|
||||||
|
|
||||||
|
var apiKey = keyConnection.Key!.Eval(this.Engine);
|
||||||
|
Throw.IfNullOrEmpty(apiKey, "The connection key must be specified in the agent definition to create an OpenAI client.");
|
||||||
|
|
||||||
|
var clientOptions = new OpenAIClientOptions();
|
||||||
|
var endpoint = keyConnection.Endpoint?.Eval(this.Engine);
|
||||||
|
if (!string.IsNullOrEmpty(endpoint))
|
||||||
|
{
|
||||||
|
clientOptions.Endpoint = new Uri(endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new OpenAIClient(new ApiKeyCredential(apiKey), clientOptions);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Azure.Core;
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
using OpenAI;
|
||||||
|
using OpenAI.Assistants;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides an <see cref="AgentFactory"/> which creates instances of <see cref="AIAgent"/> using a <see cref="AssistantClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class OpenAIAssistantAgentFactory : OpenAIAgentFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenAIAssistantAgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public OpenAIAssistantAgentFactory(IList<AIFunction>? functions = null, IConfiguration? configuration = null, ILoggerFactory? loggerFactory = null) : base(configuration, loggerFactory)
|
||||||
|
{
|
||||||
|
this._functions = functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenAIAssistantAgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public OpenAIAssistantAgentFactory(AssistantClient assistantClient, IList<AIFunction>? functions = null, IConfiguration? configuration = null, ILoggerFactory? loggerFactory = null) : base(configuration, loggerFactory)
|
||||||
|
{
|
||||||
|
Throw.IfNull(assistantClient);
|
||||||
|
|
||||||
|
this._assistantClient = assistantClient;
|
||||||
|
this._functions = functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenAIAssistantAgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public OpenAIAssistantAgentFactory(Uri endpoint, TokenCredential tokenCredential, IList<AIFunction>? functions = null, IConfiguration? configuration = null, ILoggerFactory? loggerFactory = null) : base(endpoint, tokenCredential, configuration, loggerFactory)
|
||||||
|
{
|
||||||
|
this._functions = functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<AIAgent?> TryCreateAsync(GptComponentMetadata promptAgent, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
var model = promptAgent.Model as CurrentModels;
|
||||||
|
var apiType = model?.ApiType;
|
||||||
|
if (apiType?.IsUnknown() == false || apiType?.UnknownValue?.Equals(API_TYPE_ASSISTANTS, StringComparison.OrdinalIgnoreCase) == false)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = new ChatClientAgentOptions()
|
||||||
|
{
|
||||||
|
Name = promptAgent.Name,
|
||||||
|
Description = promptAgent.Description,
|
||||||
|
Instructions = promptAgent.Instructions?.ToTemplateString(),
|
||||||
|
ChatOptions = promptAgent.GetChatOptions(this._functions),
|
||||||
|
};
|
||||||
|
|
||||||
|
AssistantClient? assistantClient = this._assistantClient ?? this.CreateAssistantClient(promptAgent);
|
||||||
|
if (assistantClient is not null)
|
||||||
|
{
|
||||||
|
var modelId = promptAgent.Model?.ModelNameHint;
|
||||||
|
Throw.IfNullOrEmpty(modelId, "The model id must be specified in the agent definition to create an OpenAI Assistant.");
|
||||||
|
Throw.IfNullOrEmpty(promptAgent.Instructions?.ToTemplateString(), "The instructions must be specified in the agent definition to create an OpenAI Assistant.");
|
||||||
|
|
||||||
|
return await assistantClient.CreateAIAgentAsync(
|
||||||
|
modelId,
|
||||||
|
options
|
||||||
|
).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region private
|
||||||
|
private readonly AssistantClient? _assistantClient;
|
||||||
|
private readonly IList<AIFunction>? _functions;
|
||||||
|
|
||||||
|
private const string API_TYPE_ASSISTANTS = "ASSISTANTS";
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Azure.Core;
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
using OpenAI.Chat;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides an <see cref="AgentFactory"/> which creates instances of <see cref="AIAgent"/> using a <see cref="ChatClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class OpenAIChatAgentFactory : OpenAIAgentFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenAIChatAgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public OpenAIChatAgentFactory(IList<AIFunction>? functions = null, IConfiguration? configuration = null, ILoggerFactory? loggerFactory = null) : base(configuration, loggerFactory)
|
||||||
|
{
|
||||||
|
this._functions = functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenAIChatAgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public OpenAIChatAgentFactory(ChatClient chatClient, IList<AIFunction>? functions = null, IConfiguration? configuration = null, ILoggerFactory? loggerFactory = null) : base(configuration, loggerFactory)
|
||||||
|
{
|
||||||
|
Throw.IfNull(chatClient);
|
||||||
|
|
||||||
|
this._chatClient = chatClient;
|
||||||
|
this._functions = functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenAIChatAgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public OpenAIChatAgentFactory(Uri endpoint, TokenCredential tokenCredential, IList<AIFunction>? functions = null, IConfiguration? configuration = null, ILoggerFactory? loggerFactory = null) : base(endpoint, tokenCredential, configuration, loggerFactory)
|
||||||
|
{
|
||||||
|
this._functions = functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<AIAgent?> TryCreateAsync(GptComponentMetadata promptAgent, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
var model = promptAgent.Model as CurrentModels;
|
||||||
|
var apiType = model?.ApiType;
|
||||||
|
if (apiType?.IsUnknown() == true || apiType?.Value != ModelApiType.Chat)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = new ChatClientAgentOptions()
|
||||||
|
{
|
||||||
|
Name = promptAgent.Name,
|
||||||
|
Description = promptAgent.Description,
|
||||||
|
Instructions = promptAgent.Instructions?.ToTemplateString(),
|
||||||
|
ChatOptions = promptAgent.GetChatOptions(this._functions),
|
||||||
|
};
|
||||||
|
|
||||||
|
ChatClient? chatClient = this._chatClient ?? this.CreateChatClient(promptAgent);
|
||||||
|
if (chatClient is not null)
|
||||||
|
{
|
||||||
|
return new ChatClientAgent(
|
||||||
|
chatClient.AsIChatClient(),
|
||||||
|
options,
|
||||||
|
this.LoggerFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region private
|
||||||
|
private readonly ChatClient? _chatClient;
|
||||||
|
private readonly IList<AIFunction>? _functions;
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Azure.AI.Agents.Persistent;
|
||||||
|
using Azure.Core;
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
using OpenAI.Responses;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides an <see cref="AgentFactory"/> which creates instances of <see cref="AIAgent"/> using a <see cref="OpenAIResponseClient"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class OpenAIResponseAgentFactory : OpenAIAgentFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenAIResponseAgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public OpenAIResponseAgentFactory(IList<AIFunction>? functions = null, IConfiguration? configuration = null, ILoggerFactory? loggerFactory = null) : base(configuration, loggerFactory)
|
||||||
|
{
|
||||||
|
this._functions = functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenAIResponseAgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public OpenAIResponseAgentFactory(OpenAIResponseClient responseClient, IList<AIFunction>? functions = null, IConfiguration? configuration = null, ILoggerFactory? loggerFactory = null) : base(configuration, loggerFactory)
|
||||||
|
{
|
||||||
|
Throw.IfNull(responseClient);
|
||||||
|
|
||||||
|
this._responseClient = responseClient;
|
||||||
|
this._functions = functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="OpenAIResponseAgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public OpenAIResponseAgentFactory(Uri endpoint, TokenCredential tokenCredential, IList<AIFunction>? functions = null, IConfiguration? configuration = null, ILoggerFactory? loggerFactory = null) : base(endpoint, tokenCredential, configuration, loggerFactory)
|
||||||
|
{
|
||||||
|
this._functions = functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<AIAgent?> TryCreateAsync(GptComponentMetadata promptAgent, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
var model = promptAgent.Model as CurrentModels;
|
||||||
|
var apiType = model?.ApiType;
|
||||||
|
if (apiType?.IsUnknown() == true || apiType?.Value != ModelApiType.Responses)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = new ChatClientAgentOptions()
|
||||||
|
{
|
||||||
|
Name = promptAgent.Name,
|
||||||
|
Description = promptAgent.Description,
|
||||||
|
Instructions = promptAgent.Instructions?.ToTemplateString(),
|
||||||
|
ChatOptions = promptAgent.GetChatOptions(this._functions),
|
||||||
|
};
|
||||||
|
|
||||||
|
var responseClient = this._responseClient ?? this.CreateResponseClient(promptAgent);
|
||||||
|
if (responseClient is not null)
|
||||||
|
{
|
||||||
|
return new ChatClientAgent(
|
||||||
|
responseClient.AsIChatClient(),
|
||||||
|
options,
|
||||||
|
this.LoggerFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region private
|
||||||
|
private readonly OpenAIResponseClient? _responseClient;
|
||||||
|
private readonly IList<AIFunction>? _functions;
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
using Microsoft.Bot.ObjectModel.Abstractions;
|
||||||
|
using Microsoft.Bot.ObjectModel.Yaml;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper methods for creating <see cref="BotElement"/> from YAML.
|
||||||
|
/// </summary>
|
||||||
|
internal static class AgentBotElementYaml
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Convert the given YAML text to a <see cref="GptComponentMetadata"/> model.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">YAML representation of the <see cref="BotElement"/> to use to create the prompt function.</param>
|
||||||
|
/// <param name="configuration">Optional <see cref="IConfiguration"/> instance which provides environment variables to the template.</param>
|
||||||
|
[RequiresDynamicCode("Calls YamlDotNet.Serialization.DeserializerBuilder.DeserializerBuilder()")]
|
||||||
|
public static GptComponentMetadata FromYaml(string text, IConfiguration? configuration = null)
|
||||||
|
{
|
||||||
|
Throw.IfNullOrEmpty(text);
|
||||||
|
|
||||||
|
using var yamlReader = new StringReader(text);
|
||||||
|
BotElement rootElement = YamlSerializer.Deserialize<BotElement>(yamlReader) ?? throw new InvalidDataException("Text does not contain a valid agent definition.");
|
||||||
|
|
||||||
|
if (rootElement is not GptComponentMetadata promptAgent)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException($"Unsupported root element: {rootElement.GetType().Name}. Expected an {nameof(GptComponentMetadata)}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var botDefinition = WrapPromptAgentWithBot(promptAgent, configuration);
|
||||||
|
|
||||||
|
return botDefinition.Descendants().OfType<GptComponentMetadata>().First();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region private
|
||||||
|
private sealed class AgentFeatureConfiguration : IFeatureConfiguration
|
||||||
|
{
|
||||||
|
public long GetInt64Value(string settingName, long defaultValue) => defaultValue;
|
||||||
|
|
||||||
|
public string GetStringValue(string settingName, string defaultValue) => defaultValue;
|
||||||
|
|
||||||
|
public bool IsEnvironmentFeatureEnabled(string featureName, bool defaultValue) => true;
|
||||||
|
|
||||||
|
public bool IsTenantFeatureEnabled(string featureName, bool defaultValue) => defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BotDefinition WrapPromptAgentWithBot(this GptComponentMetadata element, IConfiguration? configuration = null)
|
||||||
|
{
|
||||||
|
var botBuilder =
|
||||||
|
new BotDefinition.Builder
|
||||||
|
{
|
||||||
|
Components =
|
||||||
|
{
|
||||||
|
new GptComponent.Builder
|
||||||
|
{
|
||||||
|
SchemaName = "default-schema",
|
||||||
|
Metadata = element.ToBuilder(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (configuration is not null)
|
||||||
|
{
|
||||||
|
foreach (var kvp in configuration.AsEnumerable().Where(kvp => kvp.Value is not null))
|
||||||
|
{
|
||||||
|
botBuilder.EnvironmentVariables.Add(new EnvironmentVariableDefinition.Builder()
|
||||||
|
{
|
||||||
|
SchemaName = kvp.Key,
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
DisplayName = kvp.Key,
|
||||||
|
ValueComponent = new EnvironmentVariableValue.Builder()
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid(),
|
||||||
|
Value = kvp.Value!,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return botBuilder.Build();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.PowerFx;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a factory for creating <see cref="AIAgent"/> instances.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class AgentFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configuration">The configuration.</param>
|
||||||
|
protected AgentFactory(IConfiguration? configuration = null)
|
||||||
|
{
|
||||||
|
this.Engine = new RecalcEngine();
|
||||||
|
|
||||||
|
if (configuration is not null)
|
||||||
|
{
|
||||||
|
foreach (var kvp in configuration.AsEnumerable())
|
||||||
|
{
|
||||||
|
this.Engine.UpdateVariable(kvp.Key, kvp.Value ?? string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Power Fx recalculation engine used to evaluate expressions in agent definitions.
|
||||||
|
/// This engine is configured with variables from the <see cref="IConfiguration"/> provided during construction.
|
||||||
|
/// </summary>
|
||||||
|
protected RecalcEngine Engine { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a <see cref="AIAgent"/> from the specified <see cref="GptComponentMetadata"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="promptAgent">Definition of the agent to create.</param>
|
||||||
|
/// <param name="cancellationToken">Optional cancellation token.</param>
|
||||||
|
/// <return>The created <see cref="AIAgent"/>, if null the agent type is not supported.</return>
|
||||||
|
public async Task<AIAgent> CreateAsync(GptComponentMetadata promptAgent, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
var agent = await this.TryCreateAsync(promptAgent, cancellationToken).ConfigureAwait(false);
|
||||||
|
return agent ?? throw new NotSupportedException($"Agent type {promptAgent.Kind} is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to create a <see cref="AIAgent"/> from the specified <see cref="GptComponentMetadata"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="promptAgent">Definition of the agent to create.</param>
|
||||||
|
/// <param name="cancellationToken">Optional cancellation token.</param>
|
||||||
|
/// <return>The created <see cref="AIAgent"/>, if null the agent type is not supported.</return>
|
||||||
|
public abstract Task<AIAgent?> TryCreateAsync(GptComponentMetadata promptAgent, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a <see cref="AgentFactory"/> which aggregates multiple agent factories.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AggregatorAgentFactory : AgentFactory
|
||||||
|
{
|
||||||
|
private readonly AgentFactory[] _agentFactories;
|
||||||
|
|
||||||
|
/// <summary>Initializes the instance.</summary>
|
||||||
|
/// <param name="agentFactories">Ordered <see cref="AgentFactory"/> instances to aggregate.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Where multiple <see cref="AgentFactory"/> instances are provided, the first factory that supports the <see cref="GptComponentMetadata"/> will be used.
|
||||||
|
/// </remarks>
|
||||||
|
public AggregatorAgentFactory(params AgentFactory[] agentFactories)
|
||||||
|
{
|
||||||
|
Throw.IfNullOrEmpty(agentFactories);
|
||||||
|
|
||||||
|
foreach (AgentFactory agentFactory in agentFactories)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentFactory, nameof(agentFactories));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._agentFactories = agentFactories;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override async Task<AIAgent?> TryCreateAsync(GptComponentMetadata promptAgent, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
foreach (var agentFactory in this._agentFactories)
|
||||||
|
{
|
||||||
|
var agent = await agentFactory.TryCreateAsync(promptAgent, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (agent is not null)
|
||||||
|
{
|
||||||
|
return agent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides an <see cref="AgentFactory"/> which creates instances of <see cref="ChatClientAgent"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ChatClientAgentFactory : AgentFactory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="ChatClientAgentFactory"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public ChatClientAgentFactory(IChatClient chatClient, IList<AIFunction>? functions = null, IConfiguration? configuration = null, ILoggerFactory? loggerFactory = null) : base(configuration)
|
||||||
|
{
|
||||||
|
Throw.IfNull(chatClient);
|
||||||
|
|
||||||
|
this._chatClient = chatClient;
|
||||||
|
this._functions = functions;
|
||||||
|
this._loggerFactory = loggerFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override Task<AIAgent?> TryCreateAsync(GptComponentMetadata promptAgent, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
var options = new ChatClientAgentOptions()
|
||||||
|
{
|
||||||
|
Name = promptAgent.Name,
|
||||||
|
Description = promptAgent.Description,
|
||||||
|
Instructions = promptAgent.Instructions?.ToTemplateString(),
|
||||||
|
ChatOptions = promptAgent.GetChatOptions(this._functions),
|
||||||
|
};
|
||||||
|
|
||||||
|
var agent = new ChatClientAgent(this._chatClient, options, this._loggerFactory);
|
||||||
|
|
||||||
|
return Task.FromResult<AIAgent?>(agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region private
|
||||||
|
private readonly IChatClient _chatClient;
|
||||||
|
private readonly IList<AIFunction>? _functions;
|
||||||
|
private readonly ILoggerFactory? _loggerFactory;
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="CodeInterpreterTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class CodeInterpreterToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="HostedCodeInterpreterTool"/> from a <see cref="CodeInterpreterTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="CodeInterpreterTool"/></param>
|
||||||
|
internal static HostedCodeInterpreterTool AsCodeInterpreterTool(this CodeInterpreterTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
return new HostedCodeInterpreterTool();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="FileSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class FileSearchToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create a <see cref="HostedFileSearchTool"/> from a <see cref="FileSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="FileSearchTool"/></param>
|
||||||
|
internal static HostedFileSearchTool CreateFileSearchTool(this FileSearchTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
return new HostedFileSearchTool()
|
||||||
|
{
|
||||||
|
MaximumResultCount = (int?)tool.MaximumResultCount?.LiteralValue,
|
||||||
|
Inputs = tool.VectorStoreIds?.LiteralValue.Select(id => (AIContent)new HostedVectorStoreContent(id)).ToList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="InvokeClientTaskAction"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class FunctionToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="AIFunctionDeclaration"/> from a <see cref="InvokeClientTaskAction"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If a matching function already exists in the provided list, it will be returned.
|
||||||
|
/// Otherwise, a new function declaration will be created.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="tool">Instance of <see cref="InvokeClientTaskAction"/></param>
|
||||||
|
/// <param name="functions">Instance of <see cref="IList{AIFunction}"/></param>
|
||||||
|
internal static AITool CreateOrGetAITool(this InvokeClientTaskAction tool, IList<AIFunction>? functions)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
Throw.IfNull(tool.Name);
|
||||||
|
|
||||||
|
// use the tool from the provided list if it exists
|
||||||
|
if (functions is not null)
|
||||||
|
{
|
||||||
|
var function = functions.FirstOrDefault(f => tool.Matches(f));
|
||||||
|
|
||||||
|
if (function is not null)
|
||||||
|
{
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AIFunctionFactory.CreateDeclaration(
|
||||||
|
name: tool.Name,
|
||||||
|
description: tool.Description,
|
||||||
|
jsonSchema: tool.ClientActionInputSchema?.GetSchema() ?? s_defaultSchema);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a <see cref="InvokeClientTaskAction"/> matches an <see cref="AITool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="InvokeClientTaskAction"/></param>
|
||||||
|
/// <param name="aiFunc">Instance of <see cref="AIFunction"/></param>
|
||||||
|
internal static bool Matches(this InvokeClientTaskAction tool, AIFunction aiFunc)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
Throw.IfNull(aiFunc);
|
||||||
|
|
||||||
|
return tool.Name == aiFunc.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly JsonElement s_defaultSchema = JsonDocument.Parse("{\"type\":\"object\",\"properties\":{},\"additionalProperties\":false}").RootElement;
|
||||||
|
}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="McpServerToolApprovalMode"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class McpServerToolApprovalModeExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a <see cref="McpServerToolApprovalMode"/> to a <see cref="HostedMcpServerToolApprovalMode"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mode">Instance of <see cref="McpServerToolApprovalMode"/></param>
|
||||||
|
internal static HostedMcpServerToolApprovalMode AsHostedMcpServerToolApprovalMode(this McpServerToolApprovalMode mode)
|
||||||
|
{
|
||||||
|
return mode switch
|
||||||
|
{
|
||||||
|
McpServerToolNeverRequireApprovalMode => HostedMcpServerToolApprovalMode.NeverRequire,
|
||||||
|
McpServerToolAlwaysRequireApprovalMode => HostedMcpServerToolApprovalMode.AlwaysRequire,
|
||||||
|
McpServerToolRequireSpecificApprovalMode specificMode =>
|
||||||
|
HostedMcpServerToolApprovalMode.RequireSpecific(
|
||||||
|
specificMode?.AlwaysRequireApprovalToolNames?.LiteralValue ?? [],
|
||||||
|
specificMode?.NeverRequireApprovalToolNames?.LiteralValue ?? []
|
||||||
|
),
|
||||||
|
_ => HostedMcpServerToolApprovalMode.AlwaysRequire,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="McpServerTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class McpServerToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="HostedMcpServerTool"/> from a <see cref="McpServerTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="McpServerTool"/></param>
|
||||||
|
internal static HostedMcpServerTool CreateHostedMcpTool(this McpServerTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
Throw.IfNull(tool.ServerName?.LiteralValue);
|
||||||
|
Throw.IfNull(tool.Connection);
|
||||||
|
|
||||||
|
var connection = tool.Connection as AnonymousConnection ?? throw new ArgumentException("Only AnonymousConnection is supported for MCP Server Tool connections.", nameof(tool));
|
||||||
|
var serverUrl = connection.Endpoint?.LiteralValue;
|
||||||
|
Throw.IfNullOrEmpty(serverUrl, nameof(connection.Endpoint));
|
||||||
|
|
||||||
|
return new HostedMcpServerTool(tool.ServerName.LiteralValue, serverUrl)
|
||||||
|
{
|
||||||
|
ServerDescription = tool.ServerDescription?.LiteralValue,
|
||||||
|
AllowedTools = tool.AllowedTools?.LiteralValue,
|
||||||
|
ApprovalMode = tool.ApprovalMode?.AsHostedMcpServerToolApprovalMode(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="ModelOptions"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ModelOptionsExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the 'chatToolMode' property from a <see cref="ModelOptions"/> to a <see cref="ChatToolMode"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="modelOptions">Instance of <see cref="ModelOptions"/></param>
|
||||||
|
internal static ChatToolMode? AsChatToolMode(this ModelOptions modelOptions)
|
||||||
|
{
|
||||||
|
Throw.IfNull(modelOptions);
|
||||||
|
|
||||||
|
var mode = modelOptions.ExtensionData?.GetPropertyOrNull<StringDataValue>(InitializablePropertyPath.Create("chatToolMode"))?.Value;
|
||||||
|
if (mode is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return mode switch
|
||||||
|
{
|
||||||
|
"auto" => ChatToolMode.Auto,
|
||||||
|
"none" => ChatToolMode.None,
|
||||||
|
"require_any" => ChatToolMode.RequireAny,
|
||||||
|
_ => ChatToolMode.RequireSpecific(mode),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the 'additional_properties' property from a <see cref="ModelOptions"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="modelOptions">Instance of <see cref="ModelOptions"/></param>
|
||||||
|
/// <param name="excludedProperties">List of properties which should not be included in additional properties.</param>
|
||||||
|
internal static AdditionalPropertiesDictionary? GetAdditionalProperties(this ModelOptions modelOptions, string[] excludedProperties)
|
||||||
|
{
|
||||||
|
Throw.IfNull(modelOptions);
|
||||||
|
|
||||||
|
var options = modelOptions.ExtensionData;
|
||||||
|
if (options is null || options.Properties.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var additionalProperties = options.Properties
|
||||||
|
.Where(kvp => !excludedProperties.Contains(kvp.Key))
|
||||||
|
.ToDictionary(
|
||||||
|
kvp => kvp.Key,
|
||||||
|
kvp => kvp.Value?.ToObject());
|
||||||
|
|
||||||
|
if (additionalProperties is null || additionalProperties.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AdditionalPropertiesDictionary(additionalProperties);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="GptComponentMetadata"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class PromptAgentExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the 'options' property from a <see cref="GptComponentMetadata"/> as a <see cref="ChatOptions"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="promptAgent">Instance of <see cref="GptComponentMetadata"/></param>
|
||||||
|
/// <param name="functions">Instance of <see cref="IList{AIFunction}"/></param>
|
||||||
|
public static ChatOptions? GetChatOptions(this GptComponentMetadata promptAgent, IList<AIFunction>? functions)
|
||||||
|
{
|
||||||
|
Throw.IfNull(promptAgent);
|
||||||
|
|
||||||
|
var outputSchema = promptAgent.OutputType;
|
||||||
|
var modelOptions = promptAgent.Model?.Options;
|
||||||
|
|
||||||
|
var tools = promptAgent.GetAITools(functions);
|
||||||
|
|
||||||
|
if (modelOptions is null && tools is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ChatOptions()
|
||||||
|
{
|
||||||
|
Instructions = promptAgent.ResponseInstructions?.ToTemplateString(),
|
||||||
|
Temperature = (float?)modelOptions?.Temperature?.LiteralValue,
|
||||||
|
MaxOutputTokens = (int?)modelOptions?.MaxOutputTokens?.LiteralValue,
|
||||||
|
TopP = (float?)modelOptions?.TopP?.LiteralValue,
|
||||||
|
TopK = (int?)modelOptions?.TopK?.LiteralValue,
|
||||||
|
FrequencyPenalty = (float?)modelOptions?.FrequencyPenalty?.LiteralValue,
|
||||||
|
PresencePenalty = (float?)modelOptions?.PresencePenalty?.LiteralValue,
|
||||||
|
Seed = modelOptions?.Seed?.LiteralValue,
|
||||||
|
ResponseFormat = outputSchema?.AsChatResponseFormat(),
|
||||||
|
ModelId = promptAgent.Model?.ModelNameHint,
|
||||||
|
StopSequences = modelOptions?.StopSequences,
|
||||||
|
AllowMultipleToolCalls = modelOptions?.AllowMultipleToolCalls?.LiteralValue,
|
||||||
|
ToolMode = modelOptions?.AsChatToolMode(),
|
||||||
|
Tools = tools,
|
||||||
|
AdditionalProperties = modelOptions?.GetAdditionalProperties(s_chatOptionProperties),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the 'tools' property from a <see cref="GptComponentMetadata"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="promptAgent">Instance of <see cref="GptComponentMetadata"/></param>
|
||||||
|
/// <param name="functions">Instance of <see cref="IList{AIFunction}"/></param>
|
||||||
|
internal static List<AITool>? GetAITools(this GptComponentMetadata promptAgent, IList<AIFunction>? functions)
|
||||||
|
{
|
||||||
|
return promptAgent.Tools.Select(tool =>
|
||||||
|
{
|
||||||
|
return tool switch
|
||||||
|
{
|
||||||
|
CodeInterpreterTool => ((CodeInterpreterTool)tool).AsCodeInterpreterTool(),
|
||||||
|
InvokeClientTaskAction => ((InvokeClientTaskAction)tool).CreateOrGetAITool(functions),
|
||||||
|
McpServerTool => ((McpServerTool)tool).CreateHostedMcpTool(),
|
||||||
|
FileSearchTool => ((FileSearchTool)tool).CreateFileSearchTool(),
|
||||||
|
WebSearchTool => ((WebSearchTool)tool).CreateWebSearchTool(),
|
||||||
|
_ => throw new NotSupportedException($"Unable to create tool definition because of unsupported tool type: {tool.Kind}, supported tool types are: {string.Join(",", s_validToolKinds)}"),
|
||||||
|
};
|
||||||
|
}).ToList() ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
#region private
|
||||||
|
private const string CodeInterpreterKind = "codeInterpreter";
|
||||||
|
private const string FileSearchKind = "fileSearch";
|
||||||
|
private const string FunctionKind = "function";
|
||||||
|
private const string WebSearchKind = "webSearch";
|
||||||
|
private const string McpKind = "mcp";
|
||||||
|
|
||||||
|
private static readonly string[] s_validToolKinds =
|
||||||
|
[
|
||||||
|
CodeInterpreterKind,
|
||||||
|
FileSearchKind,
|
||||||
|
FunctionKind,
|
||||||
|
WebSearchKind,
|
||||||
|
McpKind
|
||||||
|
];
|
||||||
|
|
||||||
|
private static readonly string[] s_chatOptionProperties =
|
||||||
|
[
|
||||||
|
"allowMultipleToolCalls",
|
||||||
|
"conversationId",
|
||||||
|
"chatToolMode",
|
||||||
|
"frequencyPenalty",
|
||||||
|
"additionalInstructions",
|
||||||
|
"maxOutputTokens",
|
||||||
|
"modelId",
|
||||||
|
"presencePenalty",
|
||||||
|
"responseFormat",
|
||||||
|
"seed",
|
||||||
|
"stopSequences",
|
||||||
|
"temperature",
|
||||||
|
"topK",
|
||||||
|
"topP",
|
||||||
|
"toolMode",
|
||||||
|
"tools",
|
||||||
|
];
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="PropertyInfo"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class PropertyInfoExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="Dictionary{TKey, TValue}"/> of <see cref="string"/> and <see cref="object"/>
|
||||||
|
/// from an <see cref="IReadOnlyDictionary{TKey, TValue}"/> of <see cref="string"/> and <see cref="PropertyInfo"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="properties">A read-only dictionary of property names and their corresponding <see cref="PropertyInfo"/> objects.</param>
|
||||||
|
public static Dictionary<string, object> AsObjectDictionary(this IReadOnlyDictionary<string, PropertyInfo> properties)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
foreach (var property in properties)
|
||||||
|
{
|
||||||
|
result[property.Key] = BuildPropertySchema(property.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region private
|
||||||
|
private static Dictionary<string, object> BuildPropertySchema(PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
var propertySchema = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
// Map the DataType to JSON schema type and add type-specific properties
|
||||||
|
switch (propertyInfo.Type)
|
||||||
|
{
|
||||||
|
case StringDataType:
|
||||||
|
propertySchema["type"] = "string";
|
||||||
|
break;
|
||||||
|
case NumberDataType:
|
||||||
|
propertySchema["type"] = "number";
|
||||||
|
break;
|
||||||
|
case BooleanDataType:
|
||||||
|
propertySchema["type"] = "boolean";
|
||||||
|
break;
|
||||||
|
case DateTimeDataType:
|
||||||
|
propertySchema["type"] = "string";
|
||||||
|
propertySchema["format"] = "date-time";
|
||||||
|
break;
|
||||||
|
case DateDataType:
|
||||||
|
propertySchema["type"] = "string";
|
||||||
|
propertySchema["format"] = "date";
|
||||||
|
break;
|
||||||
|
case TimeDataType:
|
||||||
|
propertySchema["type"] = "string";
|
||||||
|
propertySchema["format"] = "time";
|
||||||
|
break;
|
||||||
|
case RecordDataType nestedRecordType:
|
||||||
|
#pragma warning disable IL2026, IL3050
|
||||||
|
// For nested records, recursively build the schema
|
||||||
|
var nestedSchema = nestedRecordType.GetSchema();
|
||||||
|
var nestedJson = JsonSerializer.Serialize(nestedSchema, ElementSerializer.CreateOptions());
|
||||||
|
var nestedDict = JsonSerializer.Deserialize<Dictionary<string, object>>(nestedJson, ElementSerializer.CreateOptions());
|
||||||
|
#pragma warning restore IL2026, IL3050
|
||||||
|
if (nestedDict != null)
|
||||||
|
{
|
||||||
|
return nestedDict;
|
||||||
|
}
|
||||||
|
propertySchema["type"] = "object";
|
||||||
|
break;
|
||||||
|
case TableDataType tableType:
|
||||||
|
propertySchema["type"] = "array";
|
||||||
|
// TableDataType has Properties like RecordDataType
|
||||||
|
propertySchema["items"] = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["type"] = "object",
|
||||||
|
["properties"] = AsObjectDictionary(tableType.Properties),
|
||||||
|
["additionalProperties"] = false
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
propertySchema["type"] = "string";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add description if available
|
||||||
|
if (!string.IsNullOrEmpty(propertyInfo.Description))
|
||||||
|
{
|
||||||
|
propertySchema["description"] = propertyInfo.Description;
|
||||||
|
}
|
||||||
|
|
||||||
|
return propertySchema;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="RecordDataType"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class RecordDataTypeExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a <see cref="ChatResponseFormat"/> from a <see cref="RecordDataType"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recordDataType">Instance of <see cref="RecordDataType"/></param>
|
||||||
|
internal static ChatResponseFormat? AsChatResponseFormat(this RecordDataType recordDataType)
|
||||||
|
{
|
||||||
|
Throw.IfNull(recordDataType);
|
||||||
|
|
||||||
|
if (recordDataType.Properties.Count == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Consider adding schemaName and schemaDescription parameters to this method.
|
||||||
|
return ChatResponseFormat.ForJsonSchema(
|
||||||
|
schema: recordDataType.GetSchema(),
|
||||||
|
schemaName: recordDataType.GetSchemaName(),
|
||||||
|
schemaDescription: recordDataType.GetSchemaDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a <see cref="RecordDataType"/> to a <see cref="JsonElement"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recordDataType">Instance of <see cref="RecordDataType"/></param>
|
||||||
|
#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
|
||||||
|
#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
|
||||||
|
public static JsonElement GetSchema(this RecordDataType recordDataType)
|
||||||
|
{
|
||||||
|
Throw.IfNull(recordDataType);
|
||||||
|
|
||||||
|
var schemaObject = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
["type"] = "object",
|
||||||
|
["properties"] = recordDataType.Properties.AsObjectDictionary(),
|
||||||
|
["additionalProperties"] = false
|
||||||
|
};
|
||||||
|
|
||||||
|
var json = JsonSerializer.Serialize(schemaObject, ElementSerializer.CreateOptions());
|
||||||
|
return JsonSerializer.Deserialize<JsonElement>(json);
|
||||||
|
}
|
||||||
|
#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
|
||||||
|
#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the 'schemaName' property from a <see cref="RecordDataType"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static string? GetSchemaName(this RecordDataType recordDataType)
|
||||||
|
{
|
||||||
|
Throw.IfNull(recordDataType);
|
||||||
|
|
||||||
|
return recordDataType.ExtensionData?.GetPropertyOrNull<StringDataValue>(InitializablePropertyPath.Create("schemaName"))?.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the 'schemaDescription' property from a <see cref="RecordDataType"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static string? GetSchemaDescription(this RecordDataType recordDataType)
|
||||||
|
{
|
||||||
|
Throw.IfNull(recordDataType);
|
||||||
|
|
||||||
|
return recordDataType.ExtensionData?.GetPropertyOrNull<StringDataValue>(InitializablePropertyPath.Create("schemaDescription"))?.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="RecordDataValue"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class RecordDataValueExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a 'number' property from a <see cref="RecordDataValue"/>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recordData">Instance of <see cref="RecordDataValue"/></param>
|
||||||
|
/// <param name="propertyPath">Path of the property to retrieve</param>
|
||||||
|
public static decimal? GetNumber(this RecordDataValue recordData, string propertyPath)
|
||||||
|
{
|
||||||
|
Throw.IfNull(recordData);
|
||||||
|
|
||||||
|
var numberValue = recordData.GetPropertyOrNull<NumberDataValue>(InitializablePropertyPath.Create(propertyPath));
|
||||||
|
return numberValue?.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a nullable boolean value from the specified property path within the given record data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recordData">Instance of <see cref="RecordDataValue"/></param>
|
||||||
|
/// <param name="propertyPath">Path of the property to retrieve</param>
|
||||||
|
public static bool? GetBoolean(this RecordDataValue recordData, string propertyPath)
|
||||||
|
{
|
||||||
|
Throw.IfNull(recordData);
|
||||||
|
|
||||||
|
var booleanValue = recordData.GetPropertyOrNull<BooleanDataValue>(InitializablePropertyPath.Create(propertyPath));
|
||||||
|
return booleanValue?.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts a <see cref="RecordDataValue"/> to a <see cref="IReadOnlyDictionary{TKey, TValue}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recordData">Instance of <see cref="RecordDataValue"/></param>
|
||||||
|
public static IReadOnlyDictionary<string, string> ToDictionary(this RecordDataValue recordData)
|
||||||
|
{
|
||||||
|
Throw.IfNull(recordData);
|
||||||
|
|
||||||
|
return recordData.Properties.ToDictionary(
|
||||||
|
kvp => kvp.Key,
|
||||||
|
kvp => kvp.Value?.ToString() ?? string.Empty
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the 'schema' property from a <see cref="RecordDataValue"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="recordData">Instance of <see cref="RecordDataValue"/></param>
|
||||||
|
#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
|
||||||
|
#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
|
||||||
|
public static JsonElement? GetSchema(this RecordDataValue recordData)
|
||||||
|
{
|
||||||
|
Throw.IfNull(recordData);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var schemaStr = recordData.GetPropertyOrNull<StringDataValue>(InitializablePropertyPath.Create("json_schema.schema"));
|
||||||
|
if (schemaStr?.Value is not null)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize<JsonElement>(schemaStr.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InvalidCastException)
|
||||||
|
{
|
||||||
|
// Ignore and try next
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseFormRec = recordData.GetPropertyOrNull<RecordDataValue>(InitializablePropertyPath.Create("json_schema.schema"));
|
||||||
|
if (responseFormRec is not null)
|
||||||
|
{
|
||||||
|
var json = JsonSerializer.Serialize(responseFormRec, ElementSerializer.CreateOptions());
|
||||||
|
return JsonSerializer.Deserialize<JsonElement>(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
#pragma warning restore IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
|
||||||
|
#pragma warning restore IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
|
||||||
|
|
||||||
|
internal static object? ToObject(this DataValue? value)
|
||||||
|
{
|
||||||
|
if (value is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return value switch
|
||||||
|
{
|
||||||
|
StringDataValue s => s.Value,
|
||||||
|
NumberDataValue n => n.Value,
|
||||||
|
BooleanDataValue b => b.Value,
|
||||||
|
TableDataValue t => t.Values.Select(v => v.ToObject()).ToList(),
|
||||||
|
RecordDataValue r => r.Properties.ToDictionary(kvp => kvp.Key, kvp => kvp.Value?.ToObject()),
|
||||||
|
_ => throw new NotSupportedException($"Unsupported DataValue type: {value.GetType().FullName}"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using Microsoft.PowerFx;
|
||||||
|
using Microsoft.PowerFx.Types;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="StringExpression"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class StringExpressionExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluates the given <see cref="StringExpression"/> using the provided <see cref="RecalcEngine"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="expression">Expression to evaluate.</param>
|
||||||
|
/// <param name="engine">Recalc engine to use for evaluation.</param>
|
||||||
|
/// <returns>The evaluated string value, or null if the expression is null or cannot be evaluated.</returns>
|
||||||
|
public static string? Eval(this StringExpression? expression, RecalcEngine engine)
|
||||||
|
{
|
||||||
|
if (expression is null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expression.IsLiteral)
|
||||||
|
{
|
||||||
|
return expression.LiteralValue?.ToString();
|
||||||
|
}
|
||||||
|
else if (expression.IsExpression)
|
||||||
|
{
|
||||||
|
return engine.Eval(expression.ExpressionText!).ToString();
|
||||||
|
}
|
||||||
|
else if (expression.IsVariableReference)
|
||||||
|
{
|
||||||
|
var stringValue = engine.Eval(expression.VariableReference!.VariableName) as StringValue;
|
||||||
|
return stringValue?.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="WebSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
internal static class WebSearchToolExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create a <see cref="HostedWebSearchTool"/> from a <see cref="WebSearchTool"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tool">Instance of <see cref="WebSearchTool"/></param>
|
||||||
|
internal static HostedWebSearchTool CreateWebSearchTool(this WebSearchTool tool)
|
||||||
|
{
|
||||||
|
Throw.IfNull(tool);
|
||||||
|
|
||||||
|
return new HostedWebSearchTool();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Shared.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extension methods for <see cref="AgentFactory"/> to support YAML based agent definitions.
|
||||||
|
/// </summary>
|
||||||
|
public static class YamlAgentFactoryExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Create a <see cref="AIAgent"/> from the given agent YAML.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="agentFactory"><see cref="AgentFactory"/> which will be used to create the agent.</param>
|
||||||
|
/// <param name="agentYaml">Text string containing the YAML representation of an <see cref="AIAgent" />.</param>
|
||||||
|
/// <param name="cancellationToken">Optional cancellation token</param>
|
||||||
|
[RequiresDynamicCode("Calls YamlDotNet.Serialization.DeserializerBuilder.DeserializerBuilder()")]
|
||||||
|
public static Task<AIAgent> CreateFromYamlAsync(this AgentFactory agentFactory, string agentYaml, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
Throw.IfNull(agentFactory);
|
||||||
|
Throw.IfNullOrEmpty(agentYaml);
|
||||||
|
|
||||||
|
var agentDefinition = AgentBotElementYaml.FromYaml(agentYaml);
|
||||||
|
|
||||||
|
return agentFactory.CreateAsync(
|
||||||
|
agentDefinition,
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
|
||||||
|
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
|
||||||
|
<VersionSuffix>preview</VersionSuffix>
|
||||||
|
<NoWarn>$(NoWarn);MEAI001</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<InjectSharedThrow>true</InjectSharedThrow>
|
||||||
|
<InjectDiagnosticClassesOnLegacy>true</InjectDiagnosticClassesOnLegacy>
|
||||||
|
<InjectTrimAttributesOnLegacy>true</InjectTrimAttributesOnLegacy>
|
||||||
|
<InjectIsExternalInitOnLegacy>true</InjectIsExternalInitOnLegacy>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Import Project="$(RepoRoot)/dotnet/nuget/nuget-package.props" />
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Microsoft.Agents.AI.Abstractions\Microsoft.Agents.AI.Abstractions.csproj" />
|
||||||
|
<ProjectReference Include="..\Microsoft.Agents.AI\Microsoft.Agents.AI.csproj" />
|
||||||
|
<PackageReference Include="System.Diagnostics.DiagnosticSource" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<!-- NuGet Package Settings -->
|
||||||
|
<Title>Microsoft Agent Framework Declarative</Title>
|
||||||
|
<Description>Provides Microsoft Agent Framework support for declarative agents.</Description>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Bot.ObjectModel" />
|
||||||
|
<PackageReference Include="Microsoft.Bot.ObjectModel.Json" />
|
||||||
|
<PackageReference Include="Microsoft.Bot.ObjectModel.PowerFx" />
|
||||||
|
<PackageReference Include="Microsoft.PowerFx.Interpreter" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<InternalsVisibleTo Include="Microsoft.Agents.AI.Declarative.UnitTests" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
+2
@@ -28,6 +28,8 @@ public static class AGUIEndpointRouteBuilderExtensions
|
|||||||
/// <param name="pattern">The URL pattern for the endpoint.</param>
|
/// <param name="pattern">The URL pattern for the endpoint.</param>
|
||||||
/// <param name="aiAgent">The agent instance.</param>
|
/// <param name="aiAgent">The agent instance.</param>
|
||||||
/// <returns>An <see cref="IEndpointConventionBuilder"/> for the mapped endpoint.</returns>
|
/// <returns>An <see cref="IEndpointConventionBuilder"/> for the mapped endpoint.</returns>
|
||||||
|
[RequiresUnreferencedCode("Dynamic code may be required for this endpoint.")]
|
||||||
|
[RequiresDynamicCode("Dynamic code may be required for this endpoint.")]
|
||||||
public static IEndpointConventionBuilder MapAGUI(
|
public static IEndpointConventionBuilder MapAGUI(
|
||||||
this IEndpointRouteBuilder endpoints,
|
this IEndpointRouteBuilder endpoints,
|
||||||
[StringSyntax("route")] string pattern,
|
[StringSyntax("route")] string pattern,
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
<InternalsVisibleTo Include="Microsoft.Agents.AI.UnitTests" />
|
<InternalsVisibleTo Include="Microsoft.Agents.AI.UnitTests" />
|
||||||
<InternalsVisibleTo Include="Microsoft.Agents.AI.Hosting.UnitTests"/>
|
<InternalsVisibleTo Include="Microsoft.Agents.AI.Hosting.UnitTests"/>
|
||||||
|
<InternalsVisibleTo Include="Microsoft.Agents.AI.Declarative.UnitTests" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
namespace Shared.IntegrationTests;
|
||||||
|
|
||||||
|
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
|
||||||
|
#pragma warning disable CA1812 // Internal class that is apparently never instantiated.
|
||||||
|
|
||||||
|
internal sealed class FoundryProjectConfiguration
|
||||||
|
{
|
||||||
|
public string Endpoint { get; set; }
|
||||||
|
|
||||||
|
public string ModelId { get; set; }
|
||||||
|
|
||||||
|
public string BingConnectionId { get; set; }
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,212 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.ClientModel.Primitives;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Azure.AI.Agents;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI.AzureAI.UnitTests;
|
||||||
|
|
||||||
|
public class AzureAIChatClientTests
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verify that when the ChatOptions has a "conv_" prefixed conversation ID, the chat client uses conversation in the http requests via the chat client
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task ChatClient_UsesDefaultConversationIdAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requestTriggered = false;
|
||||||
|
using var httpHandler = new HttpHandlerAssert(async (request) =>
|
||||||
|
{
|
||||||
|
if (request.RequestUri!.PathAndQuery.Contains("openai/responses"))
|
||||||
|
{
|
||||||
|
requestTriggered = true;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
if (request.Content is not null)
|
||||||
|
{
|
||||||
|
var requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
Assert.Contains("conv_12345", requestBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetOpenAIDefaultResponseJson(), Encoding.UTF8, "application/json") };
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentResponseJson(), Encoding.UTF8, "application/json") };
|
||||||
|
});
|
||||||
|
|
||||||
|
#pragma warning disable CA5399
|
||||||
|
using var httpClient = new HttpClient(httpHandler);
|
||||||
|
#pragma warning restore CA5399
|
||||||
|
|
||||||
|
var client = new AgentClient(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
|
||||||
|
|
||||||
|
var agent = await client.GetAIAgentAsync(
|
||||||
|
new ChatClientAgentOptions
|
||||||
|
{
|
||||||
|
Name = "test-agent",
|
||||||
|
Instructions = "Test instructions",
|
||||||
|
ChatOptions = new() { ConversationId = "conv_12345" }
|
||||||
|
}, openAIClientOptions: new() { Transport = new HttpClientPipelineTransport(httpClient) });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var thread = agent.GetNewThread();
|
||||||
|
await agent.RunAsync("Hello", thread);
|
||||||
|
|
||||||
|
Assert.True(requestTriggered);
|
||||||
|
var chatClientThread = Assert.IsType<ChatClientAgentThread>(thread);
|
||||||
|
Assert.Equal("conv_12345", chatClientThread.ConversationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify that when the chat client doesn't have a default "conv_" conversation id, the chat client still uses the conversation ID in HTTP requests.
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task ChatClient_UsesPerRequestConversationId_WhenNoDefaultConversationIdIsProvidedAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requestTriggered = false;
|
||||||
|
using var httpHandler = new HttpHandlerAssert(async (request) =>
|
||||||
|
{
|
||||||
|
if (request.RequestUri!.PathAndQuery.Contains("openai/responses"))
|
||||||
|
{
|
||||||
|
requestTriggered = true;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
if (request.Content is not null)
|
||||||
|
{
|
||||||
|
var requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
Assert.Contains("conv_12345", requestBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetOpenAIDefaultResponseJson(), Encoding.UTF8, "application/json") };
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentResponseJson(), Encoding.UTF8, "application/json") };
|
||||||
|
});
|
||||||
|
|
||||||
|
#pragma warning disable CA5399
|
||||||
|
using var httpClient = new HttpClient(httpHandler);
|
||||||
|
#pragma warning restore CA5399
|
||||||
|
|
||||||
|
var client = new AgentClient(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
|
||||||
|
|
||||||
|
var agent = await client.GetAIAgentAsync(
|
||||||
|
new ChatClientAgentOptions
|
||||||
|
{
|
||||||
|
Name = "test-agent",
|
||||||
|
Instructions = "Test instructions",
|
||||||
|
}, openAIClientOptions: new() { Transport = new HttpClientPipelineTransport(httpClient) });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var thread = agent.GetNewThread();
|
||||||
|
await agent.RunAsync("Hello", thread, options: new ChatClientAgentRunOptions() { ChatOptions = new() { ConversationId = "conv_12345" } });
|
||||||
|
|
||||||
|
Assert.True(requestTriggered);
|
||||||
|
var chatClientThread = Assert.IsType<ChatClientAgentThread>(thread);
|
||||||
|
Assert.Equal("conv_12345", chatClientThread.ConversationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify that even when the chat client has a default conversation id, the chat client will prioritize the per-request conversation id provided in HTTP requests.
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task ChatClient_UsesPerRequestConversationId_EvenWhenDefaultConversationIdIsProvidedAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requestTriggered = false;
|
||||||
|
using var httpHandler = new HttpHandlerAssert(async (request) =>
|
||||||
|
{
|
||||||
|
if (request.RequestUri!.PathAndQuery.Contains("openai/responses"))
|
||||||
|
{
|
||||||
|
requestTriggered = true;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
if (request.Content is not null)
|
||||||
|
{
|
||||||
|
var requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
Assert.Contains("conv_12345", requestBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetOpenAIDefaultResponseJson(), Encoding.UTF8, "application/json") };
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentResponseJson(), Encoding.UTF8, "application/json") };
|
||||||
|
});
|
||||||
|
|
||||||
|
#pragma warning disable CA5399
|
||||||
|
using var httpClient = new HttpClient(httpHandler);
|
||||||
|
#pragma warning restore CA5399
|
||||||
|
|
||||||
|
var client = new AgentClient(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
|
||||||
|
|
||||||
|
var agent = await client.GetAIAgentAsync(
|
||||||
|
new ChatClientAgentOptions
|
||||||
|
{
|
||||||
|
Name = "test-agent",
|
||||||
|
Instructions = "Test instructions",
|
||||||
|
ChatOptions = new() { ConversationId = "conv_should_not_use_default" }
|
||||||
|
}, openAIClientOptions: new() { Transport = new HttpClientPipelineTransport(httpClient) });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var thread = agent.GetNewThread();
|
||||||
|
await agent.RunAsync("Hello", thread, options: new ChatClientAgentRunOptions() { ChatOptions = new() { ConversationId = "conv_12345" } });
|
||||||
|
|
||||||
|
Assert.True(requestTriggered);
|
||||||
|
var chatClientThread = Assert.IsType<ChatClientAgentThread>(thread);
|
||||||
|
Assert.Equal("conv_12345", chatClientThread.ConversationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verify that when the chat client is provided without a "conv_" prefixed conversation ID, the chat client uses the previous conversation ID in HTTP requests.
|
||||||
|
/// </summary>
|
||||||
|
[Fact]
|
||||||
|
public async Task ChatClient_UsesPreviousResponseId_WhenConversationIsNotPrefixedAsConvAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var requestTriggered = false;
|
||||||
|
using var httpHandler = new HttpHandlerAssert(async (request) =>
|
||||||
|
{
|
||||||
|
if (request.RequestUri!.PathAndQuery.Contains("openai/responses"))
|
||||||
|
{
|
||||||
|
requestTriggered = true;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
if (request.Content is not null)
|
||||||
|
{
|
||||||
|
var requestBody = await request.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||||
|
Assert.Contains("resp_0888a", requestBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetOpenAIDefaultResponseJson(), Encoding.UTF8, "application/json") };
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(TestDataUtil.GetAgentResponseJson(), Encoding.UTF8, "application/json") };
|
||||||
|
});
|
||||||
|
|
||||||
|
#pragma warning disable CA5399
|
||||||
|
using var httpClient = new HttpClient(httpHandler);
|
||||||
|
#pragma warning restore CA5399
|
||||||
|
|
||||||
|
var client = new AgentClient(new Uri("https://test.openai.azure.com/"), new FakeAuthenticationTokenProvider(), new() { Transport = new HttpClientPipelineTransport(httpClient) });
|
||||||
|
|
||||||
|
var agent = await client.GetAIAgentAsync(
|
||||||
|
new ChatClientAgentOptions
|
||||||
|
{
|
||||||
|
Name = "test-agent",
|
||||||
|
Instructions = "Test instructions",
|
||||||
|
}, openAIClientOptions: new() { Transport = new HttpClientPipelineTransport(httpClient) });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var thread = agent.GetNewThread();
|
||||||
|
await agent.RunAsync("Hello", thread, options: new ChatClientAgentRunOptions() { ChatOptions = new() { ConversationId = "resp_0888a" } });
|
||||||
|
|
||||||
|
Assert.True(requestTriggered);
|
||||||
|
var chatClientThread = Assert.IsType<ChatClientAgentThread>(thread);
|
||||||
|
Assert.Equal("resp_0888a46cbf2b1ff3006914596e05d08195a77c3f5187b769a7", chatClientThread.ConversationId);
|
||||||
|
}
|
||||||
|
}
|
||||||
+95
@@ -0,0 +1,95 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Azure.Identity;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI.Declarative.IntegrationTests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests for declarative agents created using <see cref="AggregatorAgentFactory"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AzureOpenAIDeclarativeAgentTests(ITestOutputHelper output) : BaseIntegrationTest(output)
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task CanCreateAndRunChatAgentAsync()
|
||||||
|
{
|
||||||
|
// 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")}.";
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var endpointUri = new Uri(this.FoundryConfiguration.Endpoint);
|
||||||
|
var tokenCredential = new AzureCliCredential();
|
||||||
|
var agentFactory = new AggregatorAgentFactory(
|
||||||
|
[
|
||||||
|
new OpenAIChatAgentFactory(endpointUri, tokenCredential),
|
||||||
|
new OpenAIResponseAgentFactory(endpointUri, tokenCredential),
|
||||||
|
new OpenAIAssistantAgentFactory(endpointUri, tokenCredential)
|
||||||
|
]);
|
||||||
|
var agentYaml = File.ReadAllText("../../../../../../agent-samples/azure/AzureOpenAIChat.yaml");
|
||||||
|
agentYaml = agentYaml.Replace("=Env.AZURE_OPENAI_DEPLOYMENT_NAME", this.FoundryConfiguration.DeploymentName);
|
||||||
|
|
||||||
|
// Create agent run options
|
||||||
|
var options = new ChatClientAgentRunOptions(new()
|
||||||
|
{
|
||||||
|
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||||
|
var response = await agent!.RunAsync("What is the weather in Cambridge, MA in °C?", options: options);
|
||||||
|
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.NotNull(response);
|
||||||
|
Assert.NotEmpty(response.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CanCreateAndRunResponsesAgentAsync()
|
||||||
|
{
|
||||||
|
// 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")}.";
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var endpointUri = new Uri(this.FoundryConfiguration.Endpoint);
|
||||||
|
var tokenCredential = new AzureCliCredential();
|
||||||
|
var agentFactory = new AggregatorAgentFactory(
|
||||||
|
[
|
||||||
|
new OpenAIChatAgentFactory(endpointUri, tokenCredential),
|
||||||
|
new OpenAIResponseAgentFactory(endpointUri, tokenCredential),
|
||||||
|
new OpenAIAssistantAgentFactory(endpointUri, tokenCredential)
|
||||||
|
]);
|
||||||
|
var agentYaml = File.ReadAllText("../../../../../../agent-samples/azure/AzureOpenAIResponses.yaml");
|
||||||
|
agentYaml = agentYaml.Replace("=Env.AZURE_OPENAI_DEPLOYMENT_NAME", this.FoundryConfiguration.DeploymentName);
|
||||||
|
|
||||||
|
// Create agent run options
|
||||||
|
var options = new ChatClientAgentRunOptions(new()
|
||||||
|
{
|
||||||
|
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||||
|
var response = await agent!.RunAsync("What is the weather in Cambridge, MA in °C?", options: options);
|
||||||
|
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.NotNull(response);
|
||||||
|
Assert.NotEmpty(response.Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Shared.IntegrationTests;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI.Declarative.IntegrationTests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for integration tests.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class BaseIntegrationTest : IDisposable
|
||||||
|
{
|
||||||
|
private IConfigurationRoot? _configuration;
|
||||||
|
private AzureAIConfiguration? _foundryConfiguration;
|
||||||
|
private OpenAIConfiguration? _openAIConfiguration;
|
||||||
|
private FoundryProjectConfiguration? _foundryProjectConfiguration;
|
||||||
|
|
||||||
|
protected IConfigurationRoot Configuration => this._configuration ??= InitializeConfig();
|
||||||
|
|
||||||
|
internal AzureAIConfiguration FoundryConfiguration
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
this._foundryConfiguration ??= this.Configuration.GetSection("AzureAI").Get<AzureAIConfiguration>();
|
||||||
|
Assert.NotNull(this._foundryConfiguration);
|
||||||
|
return this._foundryConfiguration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal OpenAIConfiguration OpenAIConfiguration
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
this._openAIConfiguration ??= this.Configuration.GetSection("OpenAI").Get<OpenAIConfiguration>();
|
||||||
|
Assert.NotNull(this._openAIConfiguration);
|
||||||
|
return this._openAIConfiguration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal FoundryProjectConfiguration FoundryProjectConfiguration
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
this._foundryProjectConfiguration ??= this.Configuration.GetSection("FoundryProject").Get<FoundryProjectConfiguration>();
|
||||||
|
Assert.NotNull(this._foundryProjectConfiguration);
|
||||||
|
return this._foundryProjectConfiguration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestOutputAdapter Output { get; }
|
||||||
|
|
||||||
|
protected BaseIntegrationTest(ITestOutputHelper output)
|
||||||
|
{
|
||||||
|
this.Output = new TestOutputAdapter(output);
|
||||||
|
Console.SetOut(this.Output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.Dispose(isDisposing: true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
if (isDisposing)
|
||||||
|
{
|
||||||
|
this.Output.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IConfigurationRoot InitializeConfig() =>
|
||||||
|
new ConfigurationBuilder()
|
||||||
|
.AddJsonFile("appsettings.Development.json", true)
|
||||||
|
.AddEnvironmentVariables()
|
||||||
|
.AddUserSecrets(Assembly.GetExecutingAssembly())
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Azure.AI.OpenAI;
|
||||||
|
using Azure.Identity;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI.Declarative.IntegrationTests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests for declarative agents created using <see cref="ChatClientAgentFactory"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ChatClientDeclarativeAgentTests(ITestOutputHelper output) : BaseIntegrationTest(output)
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task CanCreateAndRunAssistantAgentAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var chatClient = this.CreateIChatClient();
|
||||||
|
var agentFactory = new ChatClientAgentFactory(chatClient);
|
||||||
|
var agentYaml = File.ReadAllText("../../../../../../agent-samples/chatclient/Assistant.yaml");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||||
|
var response = await agent!.RunAsync("Tell me a joke about a pirate in Italian.");
|
||||||
|
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.NotNull(response);
|
||||||
|
Assert.NotEmpty(response.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CanCreateAndRunGetWeatherAgentAsync()
|
||||||
|
{
|
||||||
|
// 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")}.";
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var chatClient = this.CreateIChatClient();
|
||||||
|
var agentFactory = new ChatClientAgentFactory(chatClient, [AIFunctionFactory.Create(GetWeather, "GetWeather")]);
|
||||||
|
var agentYaml = File.ReadAllText("../../../../../../agent-samples/chatclient/GetWeather.yaml");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||||
|
var response = await agent!.RunAsync("What is the weather in Cambridge, MA in °C?");
|
||||||
|
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.NotNull(response);
|
||||||
|
Assert.NotEmpty(response.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IChatClient CreateIChatClient()
|
||||||
|
{
|
||||||
|
var endpoint = this.FoundryConfiguration.Endpoint;
|
||||||
|
var deploymentName = this.FoundryConfiguration.DeploymentName;
|
||||||
|
|
||||||
|
// Create the chat client
|
||||||
|
return new AzureOpenAIClient(
|
||||||
|
new Uri(endpoint),
|
||||||
|
new AzureCliCredential())
|
||||||
|
.GetChatClient(deploymentName)
|
||||||
|
.AsIChatClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Azure.Identity;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI.Declarative.IntegrationTests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests for declarative agents created using <see cref="FoundryPersistentAgentFactory"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class FoundryDeclarativeAgentTests(ITestOutputHelper output) : BaseIntegrationTest(output)
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task CanCreateAndRunPersistentAgentAsync()
|
||||||
|
{
|
||||||
|
// 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")}.";
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var agentFactory = new FoundryPersistentAgentFactory(new AzureCliCredential());
|
||||||
|
var agentYaml = File.ReadAllText("../../../../../../agent-samples/foundry/PersistentAgent.yaml");
|
||||||
|
agentYaml = agentYaml.Replace("=Env.AZURE_FOUNDRY_PROJECT_ENDPOINT", this.FoundryProjectConfiguration.Endpoint);
|
||||||
|
agentYaml = agentYaml.Replace("=Env.AZURE_FOUNDRY_PROJECT_MODEL_ID", this.FoundryProjectConfiguration.ModelId);
|
||||||
|
|
||||||
|
// Create agent run options
|
||||||
|
var options = new ChatClientAgentRunOptions(new()
|
||||||
|
{
|
||||||
|
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||||
|
var response = await agent!.RunAsync("What is the weather in Cambridge, MA in °C?", options: options);
|
||||||
|
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.NotNull(response);
|
||||||
|
Assert.NotEmpty(response.Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
|
||||||
|
<TargetFrameworks Condition="'$(Configuration)' == 'Debug'">$(ProjectsDebugTargetFrameworks)</TargetFrameworks>
|
||||||
|
<InjectSharedIntegrationTestCode>True</InjectSharedIntegrationTestCode>
|
||||||
|
<InjectSharedThrow>true</InjectSharedThrow>
|
||||||
|
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<InjectSharedIntegrationTestCode>true</InjectSharedIntegrationTestCode>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="FluentAssertions" />
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
|
||||||
|
<PackageReference Include="System.Linq.Async" />
|
||||||
|
<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.AzureAI\Microsoft.Agents.AI.Declarative.AzureAI.csproj" />
|
||||||
|
<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>
|
||||||
+92
@@ -0,0 +1,92 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI.Declarative.IntegrationTests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests for declarative agents created using <see cref="AggregatorAgentFactory"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class OpenAIDeclarativeAgentTests(ITestOutputHelper output) : BaseIntegrationTest(output)
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task CanCreateAndRunChatAgentAsync()
|
||||||
|
{
|
||||||
|
// 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")}.";
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var agentFactory = new AggregatorAgentFactory(
|
||||||
|
[
|
||||||
|
new OpenAIChatAgentFactory(),
|
||||||
|
new OpenAIResponseAgentFactory(),
|
||||||
|
new OpenAIAssistantAgentFactory()
|
||||||
|
]);
|
||||||
|
var agentYaml = File.ReadAllText("../../../../../../agent-samples/openai/OpenAIChat.yaml");
|
||||||
|
agentYaml = agentYaml.Replace("=Env.OPENAI_APIKEY", this.OpenAIConfiguration.ApiKey);
|
||||||
|
agentYaml = agentYaml.Replace("=Env.OPENAI_MODEL", this.OpenAIConfiguration.ChatModelId);
|
||||||
|
|
||||||
|
// Create agent run options
|
||||||
|
var options = new ChatClientAgentRunOptions(new()
|
||||||
|
{
|
||||||
|
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||||
|
var response = await agent!.RunAsync("What is the weather in Cambridge, MA in °C?", options: options);
|
||||||
|
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.NotNull(response);
|
||||||
|
Assert.NotEmpty(response.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CanCreateAndRunResponsesAgentAsync()
|
||||||
|
{
|
||||||
|
// 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")}.";
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
var agentFactory = new AggregatorAgentFactory(
|
||||||
|
[
|
||||||
|
new OpenAIChatAgentFactory(),
|
||||||
|
new OpenAIResponseAgentFactory(),
|
||||||
|
new OpenAIAssistantAgentFactory()
|
||||||
|
]);
|
||||||
|
var agentYaml = File.ReadAllText("../../../../../../agent-samples/openai/OpenAIResponses.yaml");
|
||||||
|
agentYaml = agentYaml.Replace("=Env.OPENAI_APIKEY", this.OpenAIConfiguration.ApiKey);
|
||||||
|
agentYaml = agentYaml.Replace("=Env.OPENAI_MODEL", this.OpenAIConfiguration.ChatModelId);
|
||||||
|
|
||||||
|
// Create agent run options
|
||||||
|
var options = new ChatClientAgentRunOptions(new()
|
||||||
|
{
|
||||||
|
Tools = [AIFunctionFactory.Create(GetWeather, name: nameof(GetWeather))]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var agent = await agentFactory.CreateFromYamlAsync(agentYaml);
|
||||||
|
var response = await agent!.RunAsync("What is the weather in Cambridge, MA in °C?", options: options);
|
||||||
|
this.Output.WriteLine($"Agent Response: {response.Text}");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.NotNull(response);
|
||||||
|
Assert.NotEmpty(response.Text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI.Declarative.IntegrationTests;
|
||||||
|
|
||||||
|
public sealed class TestOutputAdapter(ITestOutputHelper output) : TextWriter, ILogger, ILoggerFactory
|
||||||
|
{
|
||||||
|
private readonly Stack<string> _scopes = [];
|
||||||
|
|
||||||
|
public override Encoding Encoding { get; } = Encoding.UTF8;
|
||||||
|
|
||||||
|
public void AddProvider(ILoggerProvider provider) => throw new NotSupportedException();
|
||||||
|
|
||||||
|
public ILogger CreateLogger(string categoryName) => this;
|
||||||
|
|
||||||
|
public bool IsEnabled(LogLevel logLevel) => true;
|
||||||
|
|
||||||
|
public override void WriteLine(object? value) => this.SafeWrite($"{value}");
|
||||||
|
|
||||||
|
public override void WriteLine(string? format, params object?[] arg) => this.SafeWrite(string.Format(format ?? string.Empty, arg));
|
||||||
|
|
||||||
|
public override void WriteLine(string? value) => this.SafeWrite(value ?? string.Empty);
|
||||||
|
|
||||||
|
public override void Write(object? value) => this.SafeWrite($"{value}");
|
||||||
|
|
||||||
|
public override void Write(char[]? buffer) => this.SafeWrite(new string(buffer));
|
||||||
|
|
||||||
|
public IDisposable BeginScope<TState>(TState state) where TState : notnull
|
||||||
|
{
|
||||||
|
this._scopes.Push($"{state}");
|
||||||
|
return new LoggerScope(() => this._scopes.Pop());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
|
||||||
|
{
|
||||||
|
string message = formatter(state, exception);
|
||||||
|
string scope = this._scopes.Count > 0 ? $"[{this._scopes.Peek()}] " : string.Empty;
|
||||||
|
output.WriteLine($"{scope}{message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SafeWrite(string value)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
output.WriteLine(value ?? string.Empty);
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException exception) when (exception.Message == "There is no currently active test.")
|
||||||
|
{
|
||||||
|
// This exception is thrown when the test output is accessed outside of a test context.
|
||||||
|
// We can ignore it since we are not in a test context.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class LoggerScope(Action action) : IDisposable
|
||||||
|
{
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!this._disposed)
|
||||||
|
{
|
||||||
|
action.Invoke();
|
||||||
|
this._disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,268 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.PowerFx;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI.Declarative.UnitTests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unit tests for <see cref="AgentBotElementYaml"/>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class AgentBotElementYamlTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(PromptAgents.AgentWithEverything)]
|
||||||
|
[InlineData(PromptAgents.AgentWithApiKeyConnection)]
|
||||||
|
[InlineData(PromptAgents.AgentWithEnvironmentVariables)]
|
||||||
|
[InlineData(PromptAgents.AgentWithOutputSchema)]
|
||||||
|
[InlineData(PromptAgents.OpenAIChatAgent)]
|
||||||
|
[InlineData(PromptAgents.AgentWithCurrentModels)]
|
||||||
|
[InlineData(PromptAgents.AgentWithRemoteConnection)]
|
||||||
|
public void FromYaml_DoesNotThrow(string text)
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var agent = AgentBotElementYaml.FromYaml(text);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromYaml_Properties()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEverything);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.Equal("AgentName", agent.Name);
|
||||||
|
Assert.Equal("Agent description", agent.Description);
|
||||||
|
Assert.Equal("You are a helpful assistant.", agent.Instructions?.ToTemplateString());
|
||||||
|
Assert.NotNull(agent.Model);
|
||||||
|
Assert.True(agent.Tools.Length > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromYaml_CurrentModels()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithCurrentModels);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.NotNull(agent.Model);
|
||||||
|
Assert.Equal("gpt-4o", agent.Model.ModelNameHint);
|
||||||
|
Assert.NotNull(agent.Model.Options);
|
||||||
|
Assert.Equal(0.7f, (float?)agent.Model.Options?.Temperature?.LiteralValue);
|
||||||
|
Assert.Equal(0.9f, (float?)agent.Model.Options?.TopP?.LiteralValue);
|
||||||
|
|
||||||
|
// Assert contents using extension methods
|
||||||
|
Assert.Equal(1024, agent.Model.Options?.MaxOutputTokens?.LiteralValue);
|
||||||
|
Assert.Equal(50, agent.Model.Options?.TopK?.LiteralValue);
|
||||||
|
Assert.Equal(0.7f, (float?)agent.Model.Options?.FrequencyPenalty?.LiteralValue);
|
||||||
|
Assert.Equal(0.7f, (float?)agent.Model.Options?.PresencePenalty?.LiteralValue);
|
||||||
|
Assert.Equal(42, agent.Model.Options?.Seed?.LiteralValue);
|
||||||
|
Assert.Equal(PromptAgents.s_stopSequences, agent.Model.Options?.StopSequences);
|
||||||
|
Assert.True(agent.Model.Options?.AllowMultipleToolCalls?.LiteralValue);
|
||||||
|
Assert.Equal(ChatToolMode.Auto, agent.Model.Options?.AsChatToolMode());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromYaml_OutputSchema()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithOutputSchema);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.NotNull(agent.OutputType);
|
||||||
|
ChatResponseFormatJson responseFormat = (agent.OutputType.AsChatResponseFormat() as ChatResponseFormatJson)!;
|
||||||
|
Assert.NotNull(responseFormat);
|
||||||
|
Assert.NotNull(responseFormat.Schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromYaml_CodeInterpreter()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEverything);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
var tools = agent.Tools;
|
||||||
|
var codeInterpreterTools = tools.Where(t => t is CodeInterpreterTool).ToArray();
|
||||||
|
Assert.Single(codeInterpreterTools);
|
||||||
|
CodeInterpreterTool codeInterpreterTool = (codeInterpreterTools[0] as CodeInterpreterTool)!;
|
||||||
|
Assert.NotNull(codeInterpreterTool);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromYaml_FunctionTool()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEverything);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
var tools = agent.Tools;
|
||||||
|
var functionTools = tools.Where(t => t is InvokeClientTaskAction).ToArray();
|
||||||
|
Assert.Single(functionTools);
|
||||||
|
InvokeClientTaskAction functionTool = (functionTools[0] as InvokeClientTaskAction)!;
|
||||||
|
Assert.NotNull(functionTool);
|
||||||
|
Assert.Equal("GetWeather", functionTool.Name);
|
||||||
|
Assert.Equal("Get the weather for a given location.", functionTool.Description);
|
||||||
|
// TODO check schema
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromYaml_MCP()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEverything);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
var tools = agent.Tools;
|
||||||
|
var mcpTools = tools.Where(t => t is McpServerTool).ToArray();
|
||||||
|
Assert.Single(mcpTools);
|
||||||
|
McpServerTool mcpTool = (mcpTools[0] as McpServerTool)!;
|
||||||
|
Assert.NotNull(mcpTool);
|
||||||
|
Assert.Equal("PersonInfoTool", mcpTool.ServerName?.LiteralValue);
|
||||||
|
AnonymousConnection connection = (mcpTool.Connection as AnonymousConnection)!;
|
||||||
|
Assert.NotNull(connection);
|
||||||
|
Assert.Equal("https://my-mcp-endpoint.com/api", connection.Endpoint?.LiteralValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromYaml_WebSearchTool()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEverything);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
var tools = agent.Tools;
|
||||||
|
var webSearchTools = tools.Where(t => t is WebSearchTool).ToArray();
|
||||||
|
Assert.Single(webSearchTools);
|
||||||
|
Assert.NotNull(webSearchTools[0] as WebSearchTool);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromYaml_FileSearchTool()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEverything);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
var tools = agent.Tools;
|
||||||
|
var fileSearchTools = tools.Where(t => t is FileSearchTool).ToArray();
|
||||||
|
Assert.Single(fileSearchTools);
|
||||||
|
FileSearchTool fileSearchTool = (fileSearchTools[0] as FileSearchTool)!;
|
||||||
|
Assert.NotNull(fileSearchTool);
|
||||||
|
|
||||||
|
// Verify vector store content property exists and has correct values
|
||||||
|
Assert.NotNull(fileSearchTool.VectorStoreIds);
|
||||||
|
Assert.Equal(3, fileSearchTool.VectorStoreIds.LiteralValue.Length);
|
||||||
|
Assert.Equal("1", fileSearchTool.VectorStoreIds.LiteralValue[0]);
|
||||||
|
Assert.Equal("2", fileSearchTool.VectorStoreIds.LiteralValue[1]);
|
||||||
|
Assert.Equal("3", fileSearchTool.VectorStoreIds.LiteralValue[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromYaml_ApiKeyConnection()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithApiKeyConnection);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.NotNull(agent.Model);
|
||||||
|
CurrentModels model = (agent.Model as CurrentModels)!;
|
||||||
|
Assert.NotNull(model);
|
||||||
|
Assert.NotNull(model.Connection);
|
||||||
|
Assert.IsType<ApiKeyConnection>(model.Connection);
|
||||||
|
ApiKeyConnection connection = (model.Connection as ApiKeyConnection)!;
|
||||||
|
Assert.NotNull(connection);
|
||||||
|
Assert.Equal("https://my-azure-openai-endpoint.openai.azure.com/", connection.Endpoint?.LiteralValue);
|
||||||
|
Assert.Equal("my-api-key", connection.Key?.LiteralValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromYaml_RemoteConnection()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithRemoteConnection);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.NotNull(agent.Model);
|
||||||
|
CurrentModels model = (agent.Model as CurrentModels)!;
|
||||||
|
Assert.NotNull(model);
|
||||||
|
Assert.NotNull(model.Connection);
|
||||||
|
Assert.IsType<RemoteConnection>(model.Connection);
|
||||||
|
RemoteConnection connection = (model.Connection as RemoteConnection)!;
|
||||||
|
Assert.NotNull(connection);
|
||||||
|
Assert.Equal("https://my-azure-openai-endpoint.openai.azure.com/", connection.Endpoint?.LiteralValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FromYaml_WithEnvironmentVariables()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
IConfiguration configuration = new ConfigurationBuilder()
|
||||||
|
.AddInMemoryCollection(new Dictionary<string, string?>
|
||||||
|
{
|
||||||
|
["OpenAIEndpoint"] = "endpoint",
|
||||||
|
["OpenAIApiKey"] = "apiKey"
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var agent = AgentBotElementYaml.FromYaml(PromptAgents.AgentWithEnvironmentVariables, configuration);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.NotNull(agent.Model);
|
||||||
|
CurrentModels model = (agent.Model as CurrentModels)!;
|
||||||
|
Assert.NotNull(model);
|
||||||
|
Assert.NotNull(model.Connection);
|
||||||
|
Assert.IsType<ApiKeyConnection>(model.Connection);
|
||||||
|
ApiKeyConnection connection = (model.Connection as ApiKeyConnection)!;
|
||||||
|
Assert.NotNull(connection);
|
||||||
|
Assert.Equal("endpoint", Eval(connection.Endpoint!, configuration));
|
||||||
|
Assert.Equal("apiKey", Eval(connection.Key!, configuration));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents information about a person, including their name, age, and occupation, matched to the JSON schema used in the agent.
|
||||||
|
/// </summary>
|
||||||
|
[Description("Information about a person including their name, age, and occupation")]
|
||||||
|
public sealed class PersonInfo
|
||||||
|
{
|
||||||
|
[JsonPropertyName("name")]
|
||||||
|
public string? Name { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("age")]
|
||||||
|
public int? Age { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("occupation")]
|
||||||
|
public string? Occupation { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? Eval(StringExpression expression, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
RecalcEngine engine = new();
|
||||||
|
foreach (var kvp in configuration.AsEnumerable())
|
||||||
|
{
|
||||||
|
engine.UpdateVariable(kvp.Key, kvp.Value ?? string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
return expression.Eval(engine);
|
||||||
|
}
|
||||||
|
}
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.AI;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI.Declarative.UnitTests.ChatClient;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unit tests for <see cref="ChatClientAgentFactory"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ChatClientAgentFactoryTests
|
||||||
|
{
|
||||||
|
private readonly Mock<IChatClient> _mockChatClient;
|
||||||
|
|
||||||
|
public ChatClientAgentFactoryTests()
|
||||||
|
{
|
||||||
|
this._mockChatClient = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryCreateAsync_WithChatClientInConstructor_CreatesAgentAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var promptAgent = PromptAgents.CreateTestPromptAgent();
|
||||||
|
ChatClientAgentFactory factory = new(this._mockChatClient.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
AIAgent? agent = await factory.TryCreateAsync(promptAgent);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.IsType<ChatClientAgent>(agent);
|
||||||
|
Assert.Equal("Test Agent", agent.Name);
|
||||||
|
Assert.Equal("Test Description", agent.Description);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryCreateAsync_Creates_ChatClientAgentAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var promptAgent = PromptAgents.CreateTestPromptAgent();
|
||||||
|
ChatClientAgentFactory factory = new(this._mockChatClient.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
AIAgent? agent = await factory.TryCreateAsync(promptAgent);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.IsType<ChatClientAgent>(agent);
|
||||||
|
var chatClientAgent = agent as ChatClientAgent;
|
||||||
|
Assert.NotNull(chatClientAgent);
|
||||||
|
Assert.Equal("You are a helpful assistant.", chatClientAgent.Instructions);
|
||||||
|
Assert.NotNull(chatClientAgent.ChatClient);
|
||||||
|
Assert.NotNull(chatClientAgent.ChatOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryCreateAsync_Creates_ChatOptionsAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var promptAgent = PromptAgents.CreateTestPromptAgent();
|
||||||
|
ChatClientAgentFactory factory = new(this._mockChatClient.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
AIAgent? agent = await factory.TryCreateAsync(promptAgent);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.IsType<ChatClientAgent>(agent);
|
||||||
|
var chatClientAgent = agent as ChatClientAgent;
|
||||||
|
Assert.NotNull(chatClientAgent?.ChatOptions);
|
||||||
|
Assert.Equal("Provide detailed and accurate responses.", chatClientAgent?.ChatOptions?.Instructions);
|
||||||
|
Assert.Equal(0.7F, chatClientAgent?.ChatOptions?.Temperature);
|
||||||
|
Assert.Equal(0.7F, chatClientAgent?.ChatOptions?.FrequencyPenalty);
|
||||||
|
Assert.Equal(1024, chatClientAgent?.ChatOptions?.MaxOutputTokens);
|
||||||
|
Assert.Equal(0.9F, chatClientAgent?.ChatOptions?.TopP);
|
||||||
|
Assert.Equal(50, chatClientAgent?.ChatOptions?.TopK);
|
||||||
|
Assert.Equal(0.7F, chatClientAgent?.ChatOptions?.PresencePenalty);
|
||||||
|
Assert.Equal(42L, chatClientAgent?.ChatOptions?.Seed);
|
||||||
|
Assert.NotNull(chatClientAgent?.ChatOptions?.ResponseFormat);
|
||||||
|
Assert.Equal("gpt-4o", chatClientAgent?.ChatOptions?.ModelId);
|
||||||
|
Assert.Equal(["###", "END", "STOP"], chatClientAgent?.ChatOptions?.StopSequences);
|
||||||
|
Assert.True(chatClientAgent?.ChatOptions?.AllowMultipleToolCalls);
|
||||||
|
Assert.Equal(ChatToolMode.Auto, chatClientAgent?.ChatOptions?.ToolMode);
|
||||||
|
Assert.Equal("customValue", chatClientAgent?.ChatOptions?.AdditionalProperties?["customProperty"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TryCreateAsync_Creates_ToolsAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var promptAgent = PromptAgents.CreateTestPromptAgent();
|
||||||
|
ChatClientAgentFactory factory = new(this._mockChatClient.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
AIAgent? agent = await factory.TryCreateAsync(promptAgent);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(agent);
|
||||||
|
Assert.IsType<ChatClientAgent>(agent);
|
||||||
|
var chatClientAgent = agent as ChatClientAgent;
|
||||||
|
Assert.NotNull(chatClientAgent?.ChatOptions?.Tools);
|
||||||
|
var tools = chatClientAgent?.ChatOptions?.Tools;
|
||||||
|
Assert.Equal(5, tools?.Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>$(ProjectsTargetFrameworks)</TargetFrameworks>
|
||||||
|
<NoWarn>$(NoWarn);IDE1006</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Microsoft.Agents.AI.Declarative\Microsoft.Agents.AI.Declarative.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Bot.ObjectModel" />
|
||||||
|
<PackageReference Include="Microsoft.Bot.ObjectModel.Json" />
|
||||||
|
<PackageReference Include="Microsoft.Bot.ObjectModel.PowerFx" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,326 @@
|
|||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
|
||||||
|
using Microsoft.Bot.ObjectModel;
|
||||||
|
|
||||||
|
namespace Microsoft.Agents.AI.Declarative.UnitTests;
|
||||||
|
internal static class PromptAgents
|
||||||
|
{
|
||||||
|
internal const string AgentWithEverything =
|
||||||
|
"""
|
||||||
|
kind: Prompt
|
||||||
|
name: AgentName
|
||||||
|
description: Agent description
|
||||||
|
instructions: You are a helpful assistant.
|
||||||
|
model:
|
||||||
|
id: gpt-4o
|
||||||
|
options:
|
||||||
|
temperature: 0.7
|
||||||
|
maxOutputTokens: 1024
|
||||||
|
topP: 0.9
|
||||||
|
topK: 50
|
||||||
|
frequencyPenalty: 0.0
|
||||||
|
presencePenalty: 0.0
|
||||||
|
seed: 42
|
||||||
|
responseFormat: text
|
||||||
|
stopSequences:
|
||||||
|
- "###"
|
||||||
|
- "END"
|
||||||
|
- "STOP"
|
||||||
|
allowMultipleToolCalls: true
|
||||||
|
tools:
|
||||||
|
- kind: codeInterpreter
|
||||||
|
inputs:
|
||||||
|
- kind: HostedFileContent
|
||||||
|
FileId: fileId123
|
||||||
|
- kind: function
|
||||||
|
name: GetWeather
|
||||||
|
description: Get the weather for a given location.
|
||||||
|
parameters:
|
||||||
|
- name: location
|
||||||
|
type: string
|
||||||
|
description: The city and state, e.g. San Francisco, CA
|
||||||
|
required: true
|
||||||
|
- name: unit
|
||||||
|
type: string
|
||||||
|
description: The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.
|
||||||
|
required: false
|
||||||
|
enum:
|
||||||
|
- celsius
|
||||||
|
- fahrenheit
|
||||||
|
- kind: mcp
|
||||||
|
serverName: PersonInfoTool
|
||||||
|
serverDescription: Get information about a person.
|
||||||
|
connection:
|
||||||
|
kind: AnonymousConnection
|
||||||
|
endpoint: https://my-mcp-endpoint.com/api
|
||||||
|
allowedTools:
|
||||||
|
- "GetPersonInfo"
|
||||||
|
- "UpdatePersonInfo"
|
||||||
|
- "DeletePersonInfo"
|
||||||
|
approvalMode:
|
||||||
|
kind: HostedMcpServerToolRequireSpecificApprovalMode
|
||||||
|
AlwaysRequireApprovalToolNames:
|
||||||
|
- "UpdatePersonInfo"
|
||||||
|
- "DeletePersonInfo"
|
||||||
|
NeverRequireApprovalToolNames:
|
||||||
|
- "GetPersonInfo"
|
||||||
|
- kind: webSearch
|
||||||
|
name: WebSearchTool
|
||||||
|
description: Search the web for information.
|
||||||
|
- kind: fileSearch
|
||||||
|
name: FileSearchTool
|
||||||
|
description: Search files for information.
|
||||||
|
ranker: default
|
||||||
|
scoreThreshold: 0.5
|
||||||
|
maxResults: 5
|
||||||
|
maxContentLength: 2000
|
||||||
|
vectorStoreIds:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
""";
|
||||||
|
|
||||||
|
internal const string AgentWithOutputSchema =
|
||||||
|
"""
|
||||||
|
kind: Prompt
|
||||||
|
name: Translation Assistant
|
||||||
|
description: A helpful assistant that translates text to a specified language.
|
||||||
|
model:
|
||||||
|
id: gpt-4o
|
||||||
|
options:
|
||||||
|
temperature: 0.9
|
||||||
|
topP: 0.95
|
||||||
|
instructions: You are a helpful assistant. You answer questions in {language}. You return your answers in a JSON format.
|
||||||
|
additionalInstructions: You must always respond in the specified language.
|
||||||
|
tools:
|
||||||
|
- kind: codeInterpreter
|
||||||
|
template:
|
||||||
|
format: PowerFx # Mustache is the other option
|
||||||
|
parser: None # Prompty and XML are the other options
|
||||||
|
inputSchema:
|
||||||
|
properties:
|
||||||
|
language: string
|
||||||
|
outputSchema:
|
||||||
|
properties:
|
||||||
|
language:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: The language of the answer.
|
||||||
|
answer:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: The answer text.
|
||||||
|
""";
|
||||||
|
|
||||||
|
internal const string AgentWithApiKeyConnection =
|
||||||
|
"""
|
||||||
|
kind: Prompt
|
||||||
|
name: AgentName
|
||||||
|
description: Agent description
|
||||||
|
instructions: You are a helpful assistant.
|
||||||
|
model:
|
||||||
|
id: gpt-4o
|
||||||
|
connection:
|
||||||
|
kind: ApiKey
|
||||||
|
endpoint: https://my-azure-openai-endpoint.openai.azure.com/
|
||||||
|
key: my-api-key
|
||||||
|
""";
|
||||||
|
|
||||||
|
internal const string AgentWithRemoteConnection =
|
||||||
|
"""
|
||||||
|
kind: Prompt
|
||||||
|
name: AgentName
|
||||||
|
description: Agent description
|
||||||
|
instructions: You are a helpful assistant.
|
||||||
|
model:
|
||||||
|
id: gpt-4o
|
||||||
|
connection:
|
||||||
|
kind: Remote
|
||||||
|
endpoint: https://my-azure-openai-endpoint.openai.azure.com/
|
||||||
|
""";
|
||||||
|
|
||||||
|
internal const string AgentWithEnvironmentVariables =
|
||||||
|
"""
|
||||||
|
kind: Prompt
|
||||||
|
name: AgentName
|
||||||
|
description: Agent description
|
||||||
|
instructions: You are a helpful assistant.
|
||||||
|
model:
|
||||||
|
id: gpt-4o
|
||||||
|
connection:
|
||||||
|
kind: apiKey
|
||||||
|
endpoint: =Env.OpenAIEndpoint
|
||||||
|
key: =Env.OpenAIApiKey
|
||||||
|
""";
|
||||||
|
|
||||||
|
internal const string OpenAIChatAgent =
|
||||||
|
"""
|
||||||
|
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: =Env.OPENAI_MODEL
|
||||||
|
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.
|
||||||
|
""";
|
||||||
|
|
||||||
|
internal const string AgentWithCurrentModels =
|
||||||
|
"""
|
||||||
|
kind: Prompt
|
||||||
|
name: AgentName
|
||||||
|
description: Agent description
|
||||||
|
instructions: You are a helpful assistant.
|
||||||
|
model:
|
||||||
|
id: gpt-4o
|
||||||
|
options:
|
||||||
|
temperature: 0.7
|
||||||
|
maxOutputTokens: 1024
|
||||||
|
topP: 0.9
|
||||||
|
topK: 50
|
||||||
|
frequencyPenalty: 0.7
|
||||||
|
presencePenalty: 0.7
|
||||||
|
seed: 42
|
||||||
|
responseFormat: text
|
||||||
|
stopSequences:
|
||||||
|
- "###"
|
||||||
|
- "END"
|
||||||
|
- "STOP"
|
||||||
|
allowMultipleToolCalls: true
|
||||||
|
chatToolMode: auto
|
||||||
|
""";
|
||||||
|
|
||||||
|
internal const string AgentWithCurrentModelsSnakeCase =
|
||||||
|
"""
|
||||||
|
kind: Prompt
|
||||||
|
name: AgentName
|
||||||
|
description: Agent description
|
||||||
|
instructions: You are a helpful assistant.
|
||||||
|
model:
|
||||||
|
id: gpt-4o
|
||||||
|
options:
|
||||||
|
temperature: 0.7
|
||||||
|
max_output_tokens: 1024
|
||||||
|
top_p: 0.9
|
||||||
|
top_k: 50
|
||||||
|
frequency_penalty: 0.7
|
||||||
|
presence_penalty: 0.7
|
||||||
|
seed: 42
|
||||||
|
response_format: text
|
||||||
|
stop_sequences:
|
||||||
|
- "###"
|
||||||
|
- "END"
|
||||||
|
- "STOP"
|
||||||
|
allow_multiple_tool_calls: true
|
||||||
|
chat_tool_mode: auto
|
||||||
|
""";
|
||||||
|
|
||||||
|
internal static readonly string[] s_stopSequences = ["###", "END", "STOP"];
|
||||||
|
|
||||||
|
internal static GptComponentMetadata CreateTestPromptAgent(string? publisher = "OpenAI", string? apiType = "Chat")
|
||||||
|
{
|
||||||
|
string agentYaml =
|
||||||
|
$"""
|
||||||
|
kind: Prompt
|
||||||
|
name: Test Agent
|
||||||
|
description: Test Description
|
||||||
|
instructions: You are a helpful assistant.
|
||||||
|
additionalInstructions: Provide detailed and accurate responses.
|
||||||
|
model:
|
||||||
|
id: gpt-4o
|
||||||
|
publisher: {publisher}
|
||||||
|
apiType: {apiType}
|
||||||
|
options:
|
||||||
|
modelId: gpt-4o
|
||||||
|
temperature: 0.7
|
||||||
|
maxOutputTokens: 1024
|
||||||
|
topP: 0.9
|
||||||
|
topK: 50
|
||||||
|
frequencyPenalty: 0.7
|
||||||
|
presencePenalty: 0.7
|
||||||
|
seed: 42
|
||||||
|
responseFormat: text
|
||||||
|
stopSequences:
|
||||||
|
- "###"
|
||||||
|
- "END"
|
||||||
|
- "STOP"
|
||||||
|
allowMultipleToolCalls: true
|
||||||
|
chatToolMode: auto
|
||||||
|
customProperty: customValue
|
||||||
|
connection:
|
||||||
|
kind: apiKey
|
||||||
|
endpoint: https://my-azure-openai-endpoint.openai.azure.com/
|
||||||
|
key: my-api-key
|
||||||
|
tools:
|
||||||
|
- kind: codeInterpreter
|
||||||
|
- kind: function
|
||||||
|
name: GetWeather
|
||||||
|
description: Get the weather for a given location.
|
||||||
|
parameters:
|
||||||
|
- name: location
|
||||||
|
type: string
|
||||||
|
description: The city and state, e.g. San Francisco, CA
|
||||||
|
required: true
|
||||||
|
- name: unit
|
||||||
|
type: string
|
||||||
|
description: The unit of temperature. Possible values are 'celsius' and 'fahrenheit'.
|
||||||
|
required: false
|
||||||
|
enum:
|
||||||
|
- celsius
|
||||||
|
- fahrenheit
|
||||||
|
- kind: mcp
|
||||||
|
serverName: PersonInfoTool
|
||||||
|
serverDescription: Get information about a person.
|
||||||
|
allowedTools:
|
||||||
|
- "GetPersonInfo"
|
||||||
|
- "UpdatePersonInfo"
|
||||||
|
- "DeletePersonInfo"
|
||||||
|
approvalMode:
|
||||||
|
kind: HostedMcpServerToolRequireSpecificApprovalMode
|
||||||
|
AlwaysRequireApprovalToolNames:
|
||||||
|
- "UpdatePersonInfo"
|
||||||
|
- "DeletePersonInfo"
|
||||||
|
NeverRequireApprovalToolNames:
|
||||||
|
- "GetPersonInfo"
|
||||||
|
connection:
|
||||||
|
kind: AnonymousConnection
|
||||||
|
endpoint: https://my-mcp-endpoint.com/api
|
||||||
|
- kind: webSearch
|
||||||
|
name: WebSearchTool
|
||||||
|
description: Search the web for information.
|
||||||
|
- kind: fileSearch
|
||||||
|
name: FileSearchTool
|
||||||
|
description: Search files for information.
|
||||||
|
vectorStoreIds:
|
||||||
|
- 1
|
||||||
|
- 2
|
||||||
|
- 3
|
||||||
|
outputSchema:
|
||||||
|
properties:
|
||||||
|
language:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: The language of the answer.
|
||||||
|
answer:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: The answer text.
|
||||||
|
""";
|
||||||
|
|
||||||
|
return AgentBotElementYaml.FromYaml(agentYaml);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#
|
||||||
|
# This workflow demonstrates how to use the Question action
|
||||||
|
# to request user input and confirm it matches the original input.
|
||||||
|
#
|
||||||
|
# Note: This workflow doesn't make use of any agents.
|
||||||
|
#
|
||||||
|
kind: Workflow
|
||||||
|
trigger:
|
||||||
|
|
||||||
|
kind: OnConversationStart
|
||||||
|
id: workflow_demo
|
||||||
|
actions:
|
||||||
|
|
||||||
|
# Capture original input
|
||||||
|
- kind: SetVariable
|
||||||
|
id: set_project
|
||||||
|
variable: Local.OriginalInput
|
||||||
|
value: =System.LastMessage.Text
|
||||||
|
|
||||||
|
# Request input from user
|
||||||
|
- kind: Question
|
||||||
|
id: question_confirm
|
||||||
|
alwaysPrompt: false
|
||||||
|
autoSend: false
|
||||||
|
property: Local.ConfirmedInput
|
||||||
|
prompt:
|
||||||
|
kind: Message
|
||||||
|
text:
|
||||||
|
- "CONFIRM:"
|
||||||
|
entity:
|
||||||
|
kind: StringPrebuiltEntity
|
||||||
|
|
||||||
|
# Confirm input
|
||||||
|
- kind: ConditionGroup
|
||||||
|
id: check_completion
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
# Didn't match
|
||||||
|
- condition: =Local.OriginalInput <> Local.ConfirmedInput
|
||||||
|
id: check_confirm
|
||||||
|
actions:
|
||||||
|
|
||||||
|
- kind: SendActivity
|
||||||
|
id: sendActivity_mismatch
|
||||||
|
activity: |-
|
||||||
|
"{Local.ConfirmedInput}" does not match the original input of "{Local.OriginalInput}". Please try again.
|
||||||
|
|
||||||
|
- kind: GotoAction
|
||||||
|
id: goto_again
|
||||||
|
actionId: question_confirm
|
||||||
|
|
||||||
|
# Confirmed
|
||||||
|
elseActions:
|
||||||
|
- kind: SendActivity
|
||||||
|
id: sendActivity_confirmed
|
||||||
|
activity: |-
|
||||||
|
You entered:
|
||||||
|
{Local.OriginalInput}
|
||||||
|
|
||||||
|
Confirmed input:
|
||||||
|
{Local.ConfirmedInput}
|
||||||
Reference in New Issue
Block a user