mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Declarative Agents (#1301)
* AgentFactory abstractions and ChatClient implementation * Add a getitng started sample * Update to latest M.B.OM * Add some additional samples * Work in progress * Merge latest from main * Start to add support for using different kinds of connections * Remove IsSupported * Remove IsSupported * Refactor code to create clients to support DI * Add some unit tests * Update based on the latest code review feedback * Add support for OOB tools when using persistent agent sdk * Fix sample naming * Fix error based on latest MEAI * Update M.B.OM package to latest * Update to the latest M.B.OM release * Remove some obsolete helper methods * Update to the latest M.B.OM version * Fix broken unit test * Update MCP sample * Bump to latest M.B.OM release * Update to latest M.B.OM release * Update to latest M.B.OM release * Switch to using ExternalModel * Update to latest M.B.OM * Resolve merge conflicts * All tests pass * All tests pass * Start to clean up the code * Start to clean up the code * More clean up * More clean up * More clean up * Fix apiType checks * Run dotnet format * Fix typo * Address code review feedback * Add all properties for MCP tool * Address code review feedback * Address code review feedback * Fix merge * Undo warnings * Undo test change * More copilot feedback * Make class sealed * Address additional core review feedback --------- Co-authored-by: Mark Wallace <markwallace@microsoft.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
105dc82c39
commit
aaa91954c5
+49
@@ -0,0 +1,49 @@
|
||||
// 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>
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// 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>
|
||||
/// 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));
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using Azure.AI.Agents.Persistent;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using Azure.AI.Agents.Persistent;
|
||||
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="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.", nameof(tool));
|
||||
var serverUrl = connection.Endpoint?.LiteralValue;
|
||||
Throw.IfNullOrEmpty(serverUrl, nameof(connection.Endpoint));
|
||||
|
||||
return new MCPToolDefinition(tool.ServerName?.LiteralValue, serverUrl);
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
using System;
|
||||
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="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)tool).CreateCodeInterpreterToolDefinition(),
|
||||
InvokeClientTaskAction => ((InvokeClientTaskAction)tool).CreateFunctionToolDefinition(),
|
||||
FileSearchTool => ((FileSearchTool)tool).CreateFileSearchToolDefinition(),
|
||||
WebSearchTool => ((WebSearchTool)tool).CreateBingGroundingToolDefinition(),
|
||||
McpServerTool => ((McpServerTool)tool).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;
|
||||
}
|
||||
|
||||
#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));
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// 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.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.
|
||||
/// </summary>
|
||||
public FoundryPersistentAgentFactory(PersistentAgentsClient agentClient)
|
||||
{
|
||||
Throw.IfNull(agentClient);
|
||||
|
||||
this._agentClient = agentClient;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="FoundryPersistentAgentFactory"/> class.
|
||||
/// </summary>
|
||||
public FoundryPersistentAgentFactory(TokenCredential tokenCredential)
|
||||
{
|
||||
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?.LiteralValue;
|
||||
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; } = [];
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<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.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.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,182 @@
|
||||
// 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.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(ILoggerFactory? loggerFactory)
|
||||
{
|
||||
this.LoggerFactory = loggerFactory;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="OpenAIAgentFactory"/> class.
|
||||
/// </summary>
|
||||
protected OpenAIAgentFactory(Uri endpoint, TokenCredential tokenCredential, ILoggerFactory? loggerFactory)
|
||||
{
|
||||
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 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 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 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 static 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 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 static 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 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 static 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 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 static 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?.LiteralValue;
|
||||
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?.LiteralValue;
|
||||
if (!string.IsNullOrEmpty(endpoint))
|
||||
{
|
||||
clientOptions.Endpoint = new Uri(endpoint);
|
||||
}
|
||||
|
||||
return new OpenAIClient(new ApiKeyCredential(apiKey), clientOptions);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// 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.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, ILoggerFactory? loggerFactory = null) : base(loggerFactory)
|
||||
{
|
||||
this._functions = functions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="OpenAIAssistantAgentFactory"/> class.
|
||||
/// </summary>
|
||||
public OpenAIAssistantAgentFactory(AssistantClient assistantClient, IList<AIFunction>? functions = null, ILoggerFactory? loggerFactory = null) : base(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, ILoggerFactory? loggerFactory = null) : base(endpoint, tokenCredential, 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,84 @@
|
||||
// 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.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, ILoggerFactory? loggerFactory = null) : base(loggerFactory)
|
||||
{
|
||||
this._functions = functions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="OpenAIChatAgentFactory"/> class.
|
||||
/// </summary>
|
||||
public OpenAIChatAgentFactory(ChatClient chatClient, IList<AIFunction>? functions = null, ILoggerFactory? loggerFactory = null) : base(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, ILoggerFactory? loggerFactory = null) : base(endpoint, tokenCredential, 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,84 @@
|
||||
// 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.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, ILoggerFactory? loggerFactory = null) : base(loggerFactory)
|
||||
{
|
||||
this._functions = functions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="OpenAIResponseAgentFactory"/> class.
|
||||
/// </summary>
|
||||
public OpenAIResponseAgentFactory(OpenAIResponseClient responseClient, IList<AIFunction>? functions = null, ILoggerFactory? loggerFactory = null) : base(loggerFactory)
|
||||
{
|
||||
Throw.IfNull(responseClient);
|
||||
|
||||
this._responseClient = responseClient;
|
||||
this._functions = functions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="OpenAIChatAgentFactory"/> class.
|
||||
/// </summary>
|
||||
public OpenAIResponseAgentFactory(Uri endpoint, TokenCredential tokenCredential, IList<AIFunction>? functions = null, ILoggerFactory? loggerFactory = null) : base(endpoint, tokenCredential, 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,37 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Bot.ObjectModel;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a factory for creating <see cref="AIAgent"/> instances.
|
||||
/// </summary>
|
||||
public abstract class AgentFactory
|
||||
{
|
||||
/// <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,53 @@
|
||||
// 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.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, ILoggerFactory? loggerFactory = null)
|
||||
{
|
||||
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,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="aiAgent">The agent instance.</param>
|
||||
/// <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(
|
||||
this IEndpointRouteBuilder endpoints,
|
||||
[StringSyntax("route")] string pattern,
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
<InternalsVisibleTo Include="Microsoft.Agents.AI.UnitTests" />
|
||||
<InternalsVisibleTo Include="Microsoft.Agents.AI.Hosting.UnitTests"/>
|
||||
<InternalsVisibleTo Include="Microsoft.Agents.AI.Declarative.UnitTests" />
|
||||
</ItemGroup>
|
||||
|
||||
</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; }
|
||||
}
|
||||
Reference in New Issue
Block a user