chore: import upstream snapshot with attribution
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:39:25 +08:00
commit db620d33df
5151 changed files with 925932 additions and 0 deletions
@@ -0,0 +1,56 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.PowerFx;
using Microsoft.PowerFx.Types;
namespace Microsoft.Agents.ObjectModel;
/// <summary>
/// Extension methods for <see cref="BoolExpression"/>.
/// </summary>
internal static class BoolExpressionExtensions
{
/// <summary>
/// Evaluates the given <see cref="BoolExpression"/> 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 boolean value, or null if the expression is null or cannot be evaluated.</returns>
internal static bool? Eval(this BoolExpression? expression, RecalcEngine? engine)
{
if (expression is null)
{
return null;
}
if (expression.IsLiteral)
{
return expression.LiteralValue;
}
if (engine is null)
{
return null;
}
if (expression.IsExpression)
{
return engine.Eval(expression.ExpressionText!).AsBoolean();
}
else if (expression.IsVariableReference)
{
var formulaValue = engine.Eval(expression.VariableReference!.VariableName);
if (formulaValue is BooleanValue booleanValue)
{
return booleanValue.Value;
}
if (formulaValue is StringValue stringValue && bool.TryParse(stringValue.Value, out bool result))
{
return result;
}
}
return null;
}
}
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Extensions.AI;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.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.Agents.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.Agents.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;
}
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Globalization;
using Microsoft.PowerFx;
using Microsoft.PowerFx.Types;
namespace Microsoft.Agents.ObjectModel;
/// <summary>
/// Extension methods for <see cref="IntExpression"/>.
/// </summary>
internal static class IntExpressionExtensions
{
/// <summary>
/// Evaluates the given <see cref="IntExpression"/> 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 integer value, or null if the expression is null or cannot be evaluated.</returns>
internal static long? Eval(this IntExpression? expression, RecalcEngine? engine)
{
if (expression is null)
{
return null;
}
if (expression.IsLiteral)
{
return expression.LiteralValue;
}
if (engine is null)
{
return null;
}
if (expression.IsExpression)
{
return (long)engine.Eval(expression.ExpressionText!).AsDouble();
}
else if (expression.IsVariableReference)
{
var formulaValue = engine.Eval(expression.VariableReference!.VariableName);
if (formulaValue is NumberValue numberValue)
{
return (long)numberValue.Value;
}
if (formulaValue is StringValue stringValue && int.TryParse(stringValue.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int result))
{
return result;
}
}
return null;
}
}
@@ -0,0 +1,30 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.Extensions.AI;
namespace Microsoft.Agents.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.Agents.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.Agents.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,57 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Globalization;
using Microsoft.PowerFx;
using Microsoft.PowerFx.Types;
namespace Microsoft.Agents.ObjectModel;
/// <summary>
/// Extension methods for <see cref="NumberExpression"/>.
/// </summary>
internal static class NumberExpressionExtensions
{
/// <summary>
/// Evaluates the given <see cref="NumberExpression"/> 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 number value, or null if the expression is null or cannot be evaluated.</returns>
internal static double? Eval(this NumberExpression? expression, RecalcEngine? engine)
{
if (expression is null)
{
return null;
}
if (expression.IsLiteral)
{
return expression.LiteralValue;
}
if (engine is null)
{
return null;
}
if (expression.IsExpression)
{
return engine.Eval(expression.ExpressionText!).AsDouble();
}
else if (expression.IsVariableReference)
{
var formulaValue = engine.Eval(expression.VariableReference!.VariableName);
if (formulaValue is NumberValue numberValue)
{
return numberValue.Value;
}
if (formulaValue is StringValue stringValue && double.TryParse(stringValue.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double result))
{
return result;
}
}
return null;
}
}
@@ -0,0 +1,114 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.AI;
using Microsoft.PowerFx;
using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.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="engine">Instance of <see cref="RecalcEngine"/></param>
/// <param name="functions">Instance of <see cref="IList{AIFunction}"/></param>
public static ChatOptions? GetChatOptions(this GptComponentMetadata promptAgent, RecalcEngine? engine, 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.Instructions?.ToTemplateString(),
Temperature = (float?)modelOptions?.Temperature?.Eval(engine),
MaxOutputTokens = (int?)modelOptions?.MaxOutputTokens?.Eval(engine),
TopP = (float?)modelOptions?.TopP?.Eval(engine),
TopK = (int?)modelOptions?.TopK?.Eval(engine),
FrequencyPenalty = (float?)modelOptions?.FrequencyPenalty?.Eval(engine),
PresencePenalty = (float?)modelOptions?.PresencePenalty?.Eval(engine),
Seed = modelOptions?.Seed?.Eval(engine),
ResponseFormat = outputSchema?.AsChatResponseFormat(),
ModelId = promptAgent.Model?.ModelNameHint,
StopSequences = modelOptions?.StopSequences,
AllowMultipleToolCalls = modelOptions?.AllowMultipleToolCalls?.Eval(engine),
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.Agents.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.Agents.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>
private 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>
private 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.Agents.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,48 @@
// Copyright (c) Microsoft. All rights reserved.
using Microsoft.PowerFx;
using Microsoft.PowerFx.Types;
namespace Microsoft.Agents.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();
}
if (engine is null)
{
return null;
}
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.Agents.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="PromptAgentFactory"/> 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="PromptAgentFactory"/> 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 PromptAgentFactory agentFactory, string agentYaml, CancellationToken cancellationToken = default)
{
Throw.IfNull(agentFactory);
Throw.IfNullOrEmpty(agentYaml);
var agentDefinition = AgentBotElementYaml.FromYaml(agentYaml);
return agentFactory.CreateAsync(
agentDefinition,
cancellationToken);
}
}