diff --git a/src/Agents/AIContextProviderFactory.cs b/src/Agents/AIContextProviderFactory.cs deleted file mode 100644 index c70de44..0000000 --- a/src/Agents/AIContextProviderFactory.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.Agents.AI; -using static Microsoft.Agents.AI.ChatClientAgentOptions; - -namespace Devlooped.Agents.AI; - -/// -/// An implementation of an factory as a class that can provide -/// the functionality to and integrates -/// more easily into a service collection. -/// -/// -/// The is a key extensibility point in Microsoft.Agents.AI, allowing -/// augmentation of instructions, messages and tools before agent execution is performed. -/// -public abstract class AIContextProviderFactory -{ - /// - /// Provides the implementation of , - /// which is invoked whenever agent threads are created or rehydrated. - /// - /// The context to potentially hydrate state from. - /// The context provider that will enhance interactions with an agent. - public abstract AIContextProvider CreateProvider(AIContextProviderFactoryContext context); -} diff --git a/src/Agents/AgentExtensions.cs b/src/Agents/AgentExtensions.cs deleted file mode 100644 index 997dc59..0000000 --- a/src/Agents/AgentExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.ComponentModel; -using System.Runtime.CompilerServices; -using Microsoft.Agents.AI; -using Microsoft.Extensions.AI; - -namespace Devlooped.Agents.AI; - -/// -/// Miscenalleous extension methods for agents. -/// -[EditorBrowsable(EditorBrowsableState.Never)] -public static class AgentExtensions -{ - /// Ensures the returned contains the as an additional property. - /// Change priority to -10 and make EditorBrowsable.Never when https://github.com/microsoft/agent-framework/issues/1574 is fixed. - [OverloadResolutionPriority(10)] - public static ChatResponse AsChatResponse(this AgentRunResponse response) - { - var chatResponse = AgentRunResponseExtensions.AsChatResponse(response); - - chatResponse.AdditionalProperties ??= []; - chatResponse.AdditionalProperties[nameof(response.AgentId)] = response.AgentId; - - return chatResponse; - } - - extension(AIAgent agent) - { - /// Gets the emoji associated with the agent, if any. - public string? Emoji => agent is not IHasAdditionalProperties additional - ? null - : additional.AdditionalProperties is null - ? null - : additional.AdditionalProperties.TryGetValue("Emoji", out var value) ? value as string : null; - } -} \ No newline at end of file diff --git a/src/Agents/AgentMarkdownExtensions.cs b/src/Agents/AgentMarkdownExtensions.cs deleted file mode 100644 index 6780c7a..0000000 --- a/src/Agents/AgentMarkdownExtensions.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.ComponentModel; -using System.Text; -using Microsoft.Extensions.Configuration; -using NetEscapades.Configuration.Yaml; - -namespace Devlooped.Agents.AI; - -[EditorBrowsable(EditorBrowsableState.Never)] -public static class AgentMarkdownExtensions -{ - /// - /// Adds an instructions markdown file with optional YAML front-matter to the configuration sources. - /// - public static IConfigurationBuilder AddAgentMarkdown(this IConfigurationBuilder builder, string path, bool optional = false, bool reloadOnChange = false) - => builder.Add(source => - { - source.Path = path; - source.Optional = optional; - source.ReloadOnChange = reloadOnChange; - source.ResolveFileProvider(); - }); - - /// - /// Adds an instructions markdown stream with optional YAML front-matter to the configuration sources. - /// - public static IConfigurationBuilder AddAgentMarkdown(this IConfigurationBuilder builder, Stream stream) - => Throw.IfNull(builder).Add((InstructionsStreamConfigurationSource source) => source.Stream = stream); - - static class InstructionsParser - { - public static Dictionary Parse(Stream stream) - { - using var reader = new StreamReader(stream); - var frontMatter = new StringBuilder(); - var line = reader.ReadLine(); - // First line must be the front-matter according to spec. - if (line == "---") - { - while ((line = reader.ReadLine()) != "---" && !reader.EndOfStream) - frontMatter.AppendLine(line); - } - - if (frontMatter.Length > 0 && line != "---") - throw new FormatException("Instructions markdown front-matter is not properly closed with '---'."); - - var instructions = reader.ReadToEnd().Trim(); - var data = new YamlConfigurationStreamParser().Parse(new MemoryStream(Encoding.UTF8.GetBytes(frontMatter.ToString()))); - if (!data.TryGetValue("id", out var value) || Convert.ToString(value) is not { Length: > 1 } id) - throw new FormatException("Instructions markdown file must contain YAML front-matter with an 'id' key that specifies the section identifier."); - - data.Remove("id"); - // id should use the config delimiter rather than dot (which is a typical mistake when coming from TOML) - id = id.Replace(".", ConfigurationPath.KeyDelimiter); - var result = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (var entry in data) - result[$"{id}{ConfigurationPath.KeyDelimiter}{entry.Key}"] = entry.Value; - - result[$"{id}{ConfigurationPath.KeyDelimiter}instructions"] = instructions; - return result; - } - } - - class InstructionsStreamConfigurationSource : StreamConfigurationSource - { - public override IConfigurationProvider Build(IConfigurationBuilder builder) => new InstructionsStreamConfigurationProvider(this); - } - - class InstructionsStreamConfigurationProvider(InstructionsStreamConfigurationSource source) : StreamConfigurationProvider(source) - { - public override void Load(Stream stream) => Data = InstructionsParser.Parse(stream); - } - - class InstructionsConfigurationSource : FileConfigurationSource - { - public override IConfigurationProvider Build(IConfigurationBuilder builder) - { - EnsureDefaults(builder); - return new InstructionsConfigurationProvider(this); - } - } - - class InstructionsConfigurationProvider(FileConfigurationSource source) : FileConfigurationProvider(source) - { - public override void Load(Stream stream) => Data = InstructionsParser.Parse(stream); - } -} diff --git a/src/Agents/Agents.csproj b/src/Agents/Agents.csproj deleted file mode 100644 index 3fb6b4d..0000000 --- a/src/Agents/Agents.csproj +++ /dev/null @@ -1,49 +0,0 @@ - - - - net8.0;net9.0;net10.0 - Preview - Devlooped.Agents.AI - $(AssemblyName) - $(AssemblyName) - Extensions for Microsoft.Agents.AI - - OSMFEULA.txt - true - true - true - - - $(NoWarn);CS0436;SYSLIB1100;SYSLIB1101;MEAI001 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Agents/ChatMessageStoreFactory.cs b/src/Agents/ChatMessageStoreFactory.cs deleted file mode 100644 index 0bf6cb8..0000000 --- a/src/Agents/ChatMessageStoreFactory.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.Agents.AI; -using static Microsoft.Agents.AI.ChatClientAgentOptions; - -namespace Devlooped.Agents.AI; - -/// -/// An implementation of a factory as a class that can provide -/// the functionality to and integrates -/// more easily into a service collection. -/// -/// -/// The is a key extensibility point in Microsoft.Agents.AI, allowing -/// storage and retrieval of chat messages. -/// -public abstract class ChatMessageStoreFactory -{ - /// - /// Provides the implementation of - /// to provide message persistence. - /// - /// The context to potentially hydrate state from. - /// The message store that will handle chat messages. - public abstract ChatMessageStore CreateStore(ChatMessageStoreFactoryContext context); -} diff --git a/src/Agents/CompositeAIContextProvider.cs b/src/Agents/CompositeAIContextProvider.cs deleted file mode 100644 index be42e3c..0000000 --- a/src/Agents/CompositeAIContextProvider.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Microsoft.Agents.AI; -using Microsoft.Extensions.AI; - -namespace Devlooped.Agents.AI; - -/// -/// Concatenates multiple instances into a single one. -/// -class CompositeAIContextProvider : AIContextProvider -{ - readonly IList providers; - readonly AIContext? staticContext; - - public CompositeAIContextProvider(IList providers) - { - this.providers = providers; - - // Special case for single provider of static contexts - if (providers.Count == 1 && providers[0] is StaticAIContextProvider staticProvider) - { - staticContext = staticProvider.Context; - return; - } - - // Special case where all providers are static - if (providers.All(x => x is StaticAIContextProvider)) - { - // Concatenate instructions from all contexts - staticContext = new(); - var instructions = new List(); - var messages = new List(); - var tools = new List(); - - foreach (var provider in providers.Cast()) - { - var ctx = provider.Context; - - if (!string.IsNullOrEmpty(ctx.Instructions)) - instructions.Add(ctx.Instructions); - - if (ctx.Messages != null) - messages.AddRange(ctx.Messages); - - if (ctx.Tools != null) - tools.AddRange(ctx.Tools); - } - - // Same separator used by M.A.AI for instructions appending from AIContext - if (instructions.Count > 0) - staticContext.Instructions = string.Join('\n', instructions); - - if (messages.Count > 0) - staticContext.Messages = messages; - - if (tools.Count > 0) - staticContext.Tools = tools; - } - } - - public override async ValueTask InvokingAsync(InvokingContext invoking, CancellationToken cancellationToken = default) - { - if (staticContext is not null) - return staticContext; - - if (providers.Count == 1) - return await providers[0].InvokingAsync(invoking, cancellationToken); - - var context = new AIContext(); - var instructions = new List(); - var messages = new List(); - var tools = new List(); - - foreach (var provider in providers) - { - var ctx = await provider.InvokingAsync(invoking, cancellationToken); - - if (!string.IsNullOrEmpty(ctx.Instructions)) - instructions.Add(ctx.Instructions); - - if (ctx.Messages != null) - messages.AddRange(ctx.Messages); - - if (ctx.Tools != null) - tools.AddRange(ctx.Tools); - } - - // Same separator used by M.A.AI for instructions appending from AIContext - if (instructions.Count > 0) - context.Instructions = string.Join('\n', instructions); - - if (messages.Count > 0) - context.Messages = messages; - - if (tools.Count > 0) - context.Tools = tools; - - return context; - } -} \ No newline at end of file diff --git a/src/Agents/ConfigurableAIAgent.cs b/src/Agents/ConfigurableAIAgent.cs deleted file mode 100644 index 4fffe3a..0000000 --- a/src/Agents/ConfigurableAIAgent.cs +++ /dev/null @@ -1,276 +0,0 @@ -using System.ComponentModel; -using System.Diagnostics; -using System.Text.Json; -using Devlooped.Extensions.AI; -using Devlooped.Extensions.AI.OpenAI; -using Microsoft.Agents.AI; -using Microsoft.Extensions.AI; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using xAI; - -namespace Devlooped.Agents.AI; - -/// -/// A configuration-driven which monitors configuration changes and -/// re-applies them to the inner agent automatically. -/// -public sealed partial class ConfigurableAIAgent : AIAgent, IHasAdditionalProperties, IDisposable -{ - readonly IServiceProvider services; - readonly IConfiguration configuration; - readonly string section; - readonly string name; - readonly ILogger logger; - readonly Action? configure; - IDisposable reloadToken; - ChatClientAgent agent; - ChatClientAgentOptions options; - IChatClient chat; - AIAgentMetadata metadata; - - public ConfigurableAIAgent(IServiceProvider services, string section, string name, Action? configure) - { - if (section.Contains('.')) - throw new ArgumentException("Section separator must be ':', not '.'"); - - this.services = Throw.IfNull(services); - this.configuration = services.GetRequiredService(); - this.logger = services.GetRequiredService>(); - this.section = Throw.IfNullOrEmpty(section); - this.name = Throw.IfNullOrEmpty(name); - this.configure = configure; - - (agent, options, chat, metadata) = Configure(configuration.GetRequiredSection(section)); - reloadToken = configuration.GetReloadToken().RegisterChangeCallback(OnReload, state: null); - } - - /// Disposes the client and stops monitoring configuration changes. - public void Dispose() => reloadToken?.Dispose(); - - /// - public override object? GetService(Type serviceType, object? serviceKey = null) => serviceType switch - { - Type t when t == typeof(ChatClientAgentOptions) => options, - Type t when t == typeof(IChatClient) => chat, - Type t when typeof(AIAgentMetadata).IsAssignableFrom(t) => metadata, - _ => agent.GetService(serviceType, serviceKey) - }; - - /// - public AdditionalPropertiesDictionary? AdditionalProperties { get; set; } - - /// - public override string Id => agent.Id; - /// - public override string? Description => agent.Description; - /// - public override string DisplayName => agent.DisplayName; - /// - public override string? Name => name; - /// - public override AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null) - => agent.DeserializeThread(serializedThread, jsonSerializerOptions); - /// - public override AgentThread GetNewThread() => agent.GetNewThread(); - /// - public override Task RunAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - => agent.RunAsync(messages, thread, options, cancellationToken); - /// - public override IAsyncEnumerable RunStreamingAsync(IEnumerable messages, AgentThread? thread = null, AgentRunOptions? options = null, CancellationToken cancellationToken = default) - => agent.RunStreamingAsync(messages, thread, options, cancellationToken); - - /// - /// Configured agent options. - /// - public ChatClientAgentOptions Options => options; - - (ChatClientAgent, ChatClientAgentOptions, IChatClient, AIAgentMetadata) Configure(IConfigurationSection configSection) - { - var options = configSection.Get(); - options?.Name ??= name; - options?.Description = options?.Description?.Dedent(); - options?.Instructions = options?.Instructions?.Dedent(); - - var properties = configSection.Get(); - if (properties is not null) - { - properties?.Remove(nameof(AgentClientOptions.Name)); - properties?.Remove(nameof(AgentClientOptions.Description)); - properties?.Remove(nameof(AgentClientOptions.Instructions)); - properties?.Remove(nameof(AgentClientOptions.Client)); - properties?.Remove(nameof(AgentClientOptions.Model)); - properties?.Remove(nameof(AgentClientOptions.Use)); - properties?.Remove(nameof(AgentClientOptions.Tools)); - - AdditionalProperties = properties; - } - - // If there was a custom id, we must validate it didn't change since that's not supported. - if (configuration[$"{section}:name"] is { } newname && newname != name) - throw new InvalidOperationException($"The name of a configured agent cannot be changed at runtime. Expected '{name}' but was '{newname}'."); - - var client = services.GetKeyedService(options?.Client - ?? throw new InvalidOperationException($"A client must be specified for agent '{name}' in configuration section '{section}'.")) - ?? services.GetKeyedService(new ServiceKey(options!.Client)) - ?? throw new InvalidOperationException($"Specified chat client '{options!.Client}' for agent '{name}' is not registered."); - - var provider = client.GetService()?.ProviderName; - ChatOptions? chat = provider == "xai" - ? configSection.GetSection("options").Get() - : configSection.GetSection("options").Get(); - - if (chat is not null) - options.ChatOptions = chat; - else if (options.Model is not null) - (options.ChatOptions ??= new()).ModelId = options.Model; - - configure?.Invoke(name, options); - - if (options.AIContextProviderFactory is null) - { - var contextFactory = services.GetKeyedService(name) ?? - services.GetService(); - - if (contextFactory is not null) - { - if (options.Use?.Count > 0 || options.Tools?.Count > 0) - throw new InvalidOperationException($"Invalid simultaneous use of keyed service {nameof(AIContextProviderFactory)} and '{section}:use/tools' in configuration."); - - options.AIContextProviderFactory = contextFactory.CreateProvider; - } - else - { - var contexts = new List(); - if (services.GetKeyedService(name) is { } contextProvider) - contexts.Add(contextProvider); - - foreach (var use in options.Use ?? []) - { - if (services.GetKeyedService(use) is { } staticContext) - { - contexts.Add(new StaticAIContextProvider(staticContext)); - continue; - } - else if (services.GetKeyedService(use) is { } dynamicContext) - { - contexts.Add(dynamicContext); - continue; - } - - // Else, look for a config section. - if (configuration.GetSection("ai:context:" + use) is { } ctxSection && - ctxSection.Get() is { } ctxConfig) - { - var configured = new AIContext(); - if (ctxConfig.Instructions is not null) - configured.Instructions = ctxConfig.Instructions.Dedent(); - if (ctxConfig.Messages is { Count: > 1 } messages) - configured.Messages = messages; - - if (ctxConfig.Tools is not null) - { - foreach (var toolName in ctxConfig.Tools) - { - var tool = services.GetKeyedService(toolName) ?? - services.GetKeyedService(toolName) ?? - throw new InvalidOperationException($"Specified tool '{toolName}' for AI context '{ctxSection.Path}:tools' is not registered as a keyed {nameof(AITool)} or {nameof(AIFunction)}, and is required by agent section '{configSection.Path}'."); - - configured.Tools ??= []; - configured.Tools.Add(tool); - } - } - - contexts.Add(new StaticAIContextProvider(configured)); - continue; - } - - throw new InvalidOperationException($"Specified AI context '{use}' for agent '{name}' is not registered as either {nameof(AIContent)} or configuration section 'ai:context:{use}'."); - } - - foreach (var toolName in options.Tools ?? []) - { - var tool = services.GetKeyedService(toolName) ?? - services.GetKeyedService(toolName) ?? - throw new InvalidOperationException($"Specified tool '{toolName}' for agent '{section}' is not registered as a keyed {nameof(AITool)}, {nameof(AIFunction)} or MCP server tools."); - - contexts.Add(new StaticAIContextProvider(new AIContext { Tools = [tool] })); - } - - options.AIContextProviderFactory = _ => new CompositeAIContextProvider(contexts); - } - } - else if (options.Use?.Count > 0) - { - throw new InvalidOperationException($"Invalid simultaneous use of {nameof(ChatClientAgentOptions)}.{nameof(ChatClientAgentOptions.AIContextProviderFactory)} and '{section}:use' in configuration."); - } - - if (options.ChatMessageStoreFactory is null) - { - var storeFactory = services.GetKeyedService(name) ?? - services.GetService(); - - if (storeFactory is not null) - options.ChatMessageStoreFactory = storeFactory.CreateStore; - } - - LogConfigured(name); - - var agent = new ChatClientAgent(client, options, services.GetRequiredService(), services); - var metadata = agent.GetService() ?? new AIAgentMetadata(provider); - - return (agent, options, client, new ConfigurableAIAgentMetadata(name, section, metadata.ProviderName)); - } - - void OnReload(object? state) - { - var configSection = configuration.GetRequiredSection(section); - reloadToken?.Dispose(); - chat?.Dispose(); - (agent, options, chat, metadata) = Configure(configSection); - reloadToken = configuration.GetReloadToken().RegisterChangeCallback(OnReload, state: null); - } - - [LoggerMessage(LogLevel.Information, "AIAgent '{Id}' configured.")] - private partial void LogConfigured(string id); - - internal class AgentClientOptions : ChatClientAgentOptions - { - public string? Client { get; set; } - public string? Model { get; set; } - public IList? Use { get; set; } - public IList? Tools { get; set; } - } -} - -/// Metadata for a . - -[DebuggerDisplay("Name = {Name}, Section = {ConfigurationSection}, ProviderName = {ProviderName}")] -public class ConfigurableAIAgentMetadata(string name, string configurationSection, string? providerName) : AIAgentMetadata(providerName) -{ - /// Name of the agent. - public string Name => name; - /// Configuration section where the agent is defined. - public string ConfigurationSection = configurationSection; -} - -class AIContextConfiguration -{ - public string? Instructions { get; set; } - - public IList? Messages => - MessageConfigurations?.Select(config => - config.System is not null ? new ChatMessage(ChatRole.System, config.System) : - config.User is not null ? new ChatMessage(ChatRole.User, config.User) : - config.Assistant is not null ? new ChatMessage(ChatRole.Assistant, config.Assistant) : - null).Where(x => x is not null).Cast().ToList(); - - public IList? Tools { get; set; } - - [EditorBrowsable(EditorBrowsableState.Never)] - [ConfigurationKeyName("Messages")] - public MessageConfiguration[]? MessageConfigurations { get; set; } -} - -record MessageConfiguration(string? System = default, string? User = default, string? Assistant = default); diff --git a/src/Agents/ConfigurableAgentsExtensions.cs b/src/Agents/ConfigurableAgentsExtensions.cs deleted file mode 100644 index c46e110..0000000 --- a/src/Agents/ConfigurableAgentsExtensions.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Text.Json; -using Devlooped.Agents.AI; -using Devlooped.Extensions.AI; -using Microsoft.Agents.AI; -using Microsoft.Agents.AI.Hosting; -using Microsoft.Extensions.AI; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; -using ModelContextProtocol.Server; - -namespace Microsoft.Extensions.DependencyInjection; - -/// -/// Adds configuration-driven agents to an application host. -/// -[EditorBrowsable(EditorBrowsableState.Never)] -public static class ConfigurableAgentsExtensions -{ - /// Adds instances to the service collection backing . - /// The tool type. - /// The builder instance. - /// The serializer options governing tool parameter marshalling. - /// The builder provided in . - /// is . - /// - /// This method discovers all instance and static methods (public and non-public) on the specified - /// type, where the methods are attributed as , and adds an - /// instance for each. For instance methods, an instance will be constructed for each invocation of the tool. - /// - public static IAIAgentsBuilder WithTools<[DynamicallyAccessedMembers( - DynamicallyAccessedMemberTypes.PublicMethods | - DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicConstructors)] TToolType>( - this IAIAgentsBuilder builder, - JsonSerializerOptions? serializerOptions = null, - ServiceLifetime lifetime = ServiceLifetime.Singleton) - { - Throw.IfNull(builder); - - // Preserve existing registration if any, such as when using Devlooped.Extensions.DependencyInjection - // via [Service] attribute or by convention. - builder.Services.TryAdd(ServiceDescriptor.Describe(typeof(TToolType), typeof(TToolType), lifetime)); - - serializerOptions ??= ToolJsonOptions.Default; - - foreach (var toolMethod in typeof(TToolType).GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance)) - { - if (toolMethod.GetCustomAttribute() is { } toolAttribute) - { - var function = toolMethod.IsStatic - ? AIFunctionFactory.Create(toolMethod, null, - toolAttribute.Name ?? ToolJsonOptions.Default.PropertyNamingPolicy!.ConvertName(toolMethod.Name), - serializerOptions: serializerOptions) - : AIFunctionFactory.Create(toolMethod, args => args.Services?.GetRequiredService(typeof(TToolType)) ?? - throw new InvalidOperationException("Could not determine target instance for tool."), - new AIFunctionFactoryOptions - { - Name = toolAttribute.Name ?? ToolJsonOptions.Default.PropertyNamingPolicy!.ConvertName(toolMethod.Name), - SerializerOptions = serializerOptions - }); - - builder.Services.TryAdd(ServiceDescriptor.DescribeKeyed( - typeof(AIFunction), function.Name, - (_, _) => function, lifetime)); - } - } - - return builder; - } - - /// - /// Adds AI agents to the host application builder based on configuration. - /// - /// The host application builder. - /// Optional action to configure the pipeline for each agent. - /// Optional action to configure options for each agent. - /// The configuration prefix for agents, defaults to "ai:agents". - /// The host application builder with AI agents added. - public static IAIAgentsBuilder AddAIAgents(this IHostApplicationBuilder builder, Action? configurePipeline = default, Action? configureOptions = default, string prefix = "ai:agents") - { - builder.AddChatClients(); - - foreach (var entry in builder.Configuration.AsEnumerable().Where(x => - x.Key.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) && - x.Key.EndsWith("client", StringComparison.OrdinalIgnoreCase))) - { - var section = string.Join(':', entry.Key.Split(':')[..^1]); - // key == name (unlike chat clients, the AddAIAgent expects the key to be the name). - var name = builder.Configuration[$"{section}:name"] ?? section[(prefix.Length + 1)..]; - - var options = builder.Configuration.GetRequiredSection(section).Get(); - // We need logging set up for the configurable client to log changes - builder.Services.AddLogging(); - - builder.AddAIAgent(name, (sp, key) => - { - var agent = new ConfigurableAIAgent(sp, section, key, configureOptions); - - if (configurePipeline is not null) - { - var builder = agent.AsBuilder(); - configurePipeline(key, builder); - return builder.Build(sp); - } - - return agent; - }); - - // Also register for case-insensitive lookup, but without duplicating the entry in - // the AgentCatalog, since that will always resolve from above. - builder.Services.TryAdd(ServiceDescriptor.KeyedSingleton(new ServiceKey(name), (sp, key) - => sp.GetRequiredKeyedService(name))); - } - - return new DefaultAIAgentsBuilder(builder); - } - - /// Gets an AI agent by name (case-insensitive) from the service provider. - public static AIAgent? GetIAAgent(this IServiceProvider services, string name) - => services.GetKeyedService(name) ?? services.GetKeyedService(new ServiceKey(name)); -} \ No newline at end of file diff --git a/src/Agents/Extensions/.editorconfig b/src/Agents/Extensions/.editorconfig deleted file mode 100644 index f93cc99..0000000 --- a/src/Agents/Extensions/.editorconfig +++ /dev/null @@ -1,2 +0,0 @@ -[*.cs] -generated_code = true \ No newline at end of file diff --git a/src/Agents/Extensions/Dedent.cs b/src/Agents/Extensions/Dedent.cs deleted file mode 100644 index 920f671..0000000 --- a/src/Agents/Extensions/Dedent.cs +++ /dev/null @@ -1,130 +0,0 @@ -// -#region License -// MIT License -// -// Copyright (c) Daniel Cazzulino -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -#nullable enable -using System.Text; -using System.Text.RegularExpressions; - -namespace System -{ - /// - /// String extension methods for text processing. - /// - static partial class StringExtensions - { - /// - /// Remove leading whitespace from each line of a multi-line string that is common - /// to all non-empty lines. This is equivalent to Python's textwrap.dedent(). - /// - /// The text to dedent. - /// The dedented text. - /// - /// - /// var text = """ - /// Line 1 - /// Line 2 - /// Line 3 - /// """; - /// var dedented = text.Dedent(); - /// // Result: - /// // Line 1 - /// // Line 2 - /// // Line 3 - /// - /// - public static string Dedent(this string text) - { - if (string.IsNullOrEmpty(text)) - return text; - - // Detect the line ending style used in the input - var lineEnding = Environment.NewLine; - if (text.Contains("\r\n")) - lineEnding = "\r\n"; - else if (text.Contains('\r')) - lineEnding = "\r"; - else if (text.Contains('\n')) - lineEnding = "\n"; - - // Split using regex to properly handle different line endings - var lines = NewLineExpr().Split(text); - - // Remove leading and trailing empty lines - int start = 0; - int end = lines.Length - 1; - - while (start < lines.Length && string.IsNullOrWhiteSpace(lines[start])) - start++; - - while (end >= 0 && string.IsNullOrWhiteSpace(lines[end])) - end--; - - if (start > end) - return string.Empty; - - // Find the minimum indentation (ignoring empty lines) - int minIndent = int.MaxValue; - for (int i = start; i <= end; i++) - { - var line = lines[i]; - if (!string.IsNullOrWhiteSpace(line)) - { - int indent = 0; - while (indent < line.Length && char.IsWhiteSpace(line[indent])) - indent++; - - minIndent = Math.Min(minIndent, indent); - } - } - - if (minIndent == int.MaxValue || minIndent == 0) - minIndent = 0; - - // Remove the common indentation from all lines - var result = new StringBuilder(); - for (int i = start; i <= end; i++) - { - var line = lines[i]; - if (string.IsNullOrWhiteSpace(line)) - { - if (i < end) // Don't add newline for last empty line - result.Append(lineEnding); - } - else - { - var dedentedLine = line.Length > minIndent ? line[minIndent..] : string.Empty; - result.Append(dedentedLine); - if (i < end) - result.Append(lineEnding); - } - } - - return result.ToString(); - } - - [GeneratedRegex(@"\r\n|\r|\n")] - private static partial Regex NewLineExpr(); - } -} \ No newline at end of file diff --git a/src/Agents/Extensions/Resources.cs b/src/Agents/Extensions/Resources.cs deleted file mode 100644 index d8f32c8..0000000 --- a/src/Agents/Extensions/Resources.cs +++ /dev/null @@ -1,16 +0,0 @@ -// -using System.Globalization; - -namespace NetEscapades.Configuration.Yaml -{ - internal static class Resources - { - /// - /// A duplicate key '{0}' was found. - /// - internal static string FormatError_KeyIsDuplicated(object p0) - { - return string.Format(CultureInfo.CurrentCulture, "A duplicate key '{0}' was found.", p0); - } - } -} \ No newline at end of file diff --git a/src/Agents/Extensions/Throw.cs b/src/Agents/Extensions/Throw.cs deleted file mode 100644 index eea3e12..0000000 --- a/src/Agents/Extensions/Throw.cs +++ /dev/null @@ -1,992 +0,0 @@ -// -#region License -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// Adapted from https://github.com/dotnet/extensions/blob/main/src/Shared/Throw/Throw.cs -#endregion - -#nullable enable -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - -#pragma warning disable CA1716 -namespace System; -#pragma warning restore CA1716 - -/// -/// Defines static methods used to throw exceptions. -/// -/// -/// The main purpose is to reduce code size, improve performance, and standardize exception -/// messages. -/// -[SuppressMessage("Minor Code Smell", "S4136:Method overloads should be grouped together", Justification = "Doesn't work with the region layout")] -[SuppressMessage("Minor Code Smell", "S2333:Partial is gratuitous in this context", Justification = "Some projects add additional partial parts.")] -[SuppressMessage("Design", "CA1716", Justification = "Not part of an API")] - -#if !SHARED_PROJECT -[ExcludeFromCodeCoverage] -#endif - -static partial class Throw -{ - #region For Object - - /// - /// Throws an if the specified argument is . - /// - /// Argument type to be checked for . - /// Object to be checked for . - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNull] - public static T IfNull([NotNull] T argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument is null) - { - ArgumentNullException(paramName); - } - - return argument; - } - - /// - /// Throws an if the specified argument is , - /// or if the specified member is . - /// - /// Argument type to be checked for . - /// Member type to be checked for . - /// Argument to be checked for . - /// Object member to be checked for . - /// The name of the parameter being checked. - /// The name of the member. - /// The original value of . - /// - /// - /// Throws.IfNullOrMemberNull(myObject, myObject?.MyProperty) - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNull] - public static TMember IfNullOrMemberNull( - [NotNull] TParameter argument, - [NotNull] TMember member, - [CallerArgumentExpression(nameof(argument))] string paramName = "", - [CallerArgumentExpression(nameof(member))] string memberName = "") - { - if (argument is null) - { - ArgumentNullException(paramName); - } - - if (member is null) - { - ArgumentException(paramName, $"Member {memberName} of {paramName} is null"); - } - - return member; - } - - /// - /// Throws an if the specified member is . - /// - /// Argument type. - /// Member type to be checked for . - /// Argument to which member belongs. - /// Object member to be checked for . - /// The name of the parameter being checked. - /// The name of the member. - /// The original value of . - /// - /// - /// Throws.IfMemberNull(myObject, myObject.MyProperty) - /// - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNull] - [SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Analyzer isn't seeing the reference to 'argument' in the attribute")] - public static TMember IfMemberNull( - TParameter argument, - [NotNull] TMember member, - [CallerArgumentExpression(nameof(argument))] string paramName = "", - [CallerArgumentExpression(nameof(member))] string memberName = "") - where TParameter : notnull - { - if (member is null) - { - ArgumentException(paramName, $"Member {memberName} of {paramName} is null"); - } - - return member; - } - - #endregion - - #region For String - - /// - /// Throws either an or an - /// if the specified string is or whitespace respectively. - /// - /// String to be checked for or whitespace. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNull] - public static string IfNullOrWhitespace([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { -#if !NETCOREAPP3_1_OR_GREATER - if (argument == null) - { - ArgumentNullException(paramName); - } -#endif - - if (string.IsNullOrWhiteSpace(argument)) - { - if (argument == null) - { - ArgumentNullException(paramName); - } - else - { - ArgumentException(paramName, "Argument is whitespace"); - } - } - - return argument; - } - - /// - /// Throws an if the string is , - /// or if it is empty. - /// - /// String to be checked for or empty. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNull] - public static string IfNullOrEmpty([NotNull] string? argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { -#if !NETCOREAPP3_1_OR_GREATER - if (argument == null) - { - ArgumentNullException(paramName); - } -#endif - - if (string.IsNullOrEmpty(argument)) - { - if (argument == null) - { - ArgumentNullException(paramName); - } - else - { - ArgumentException(paramName, "Argument is an empty string"); - } - } - - return argument; - } - - #endregion - - #region For Buffer - - /// - /// Throws an if the argument's buffer size is less than the required buffer size. - /// - /// The actual buffer size. - /// The required buffer size. - /// The name of the parameter to be checked. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void IfBufferTooSmall(int bufferSize, int requiredSize, string paramName = "") - { - if (bufferSize < requiredSize) - { - ArgumentException(paramName, $"Buffer too small, needed a size of {requiredSize} but got {bufferSize}"); - } - } - - #endregion - - #region For Enums - - /// - /// Throws an if the enum value is not valid. - /// - /// The argument to evaluate. - /// The name of the parameter being checked. - /// The type of the enumeration. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T IfOutOfRange(T argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") - where T : struct, Enum - { -#if NET5_0_OR_GREATER - if (!Enum.IsDefined(argument)) -#else - if (!Enum.IsDefined(typeof(T), argument)) -#endif - { - ArgumentOutOfRangeException(paramName, $"{argument} is an invalid value for enum type {typeof(T)}"); - } - - return argument; - } - - #endregion - - #region For Collections - - /// - /// Throws an if the collection is , - /// or if it is empty. - /// - /// The collection to evaluate. - /// The name of the parameter being checked. - /// The type of objects in the collection. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [return: NotNull] - - // The method has actually 100% coverage, but due to a bug in the code coverage tool, - // a lower number is reported. Therefore, we temporarily exclude this method - // from the coverage measurements. Once the bug in the code coverage tool is fixed, - // the exclusion attribute can be removed. - [ExcludeFromCodeCoverage] - public static IEnumerable IfNullOrEmpty([NotNull] IEnumerable? argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument == null) - { - ArgumentNullException(paramName); - } - else - { - switch (argument) - { - case ICollection collection: - if (collection.Count == 0) - { - ArgumentException(paramName, "Collection is empty"); - } - - break; - case IReadOnlyCollection readOnlyCollection: - if (readOnlyCollection.Count == 0) - { - ArgumentException(paramName, "Collection is empty"); - } - - break; - default: - using (IEnumerator enumerator = argument.GetEnumerator()) - { - if (!enumerator.MoveNext()) - { - ArgumentException(paramName, "Collection is empty"); - } - } - - break; - } - } - - return argument; - } - - #endregion - - #region Exceptions - - /// - /// Throws an . - /// - /// The name of the parameter that caused the exception. -#if !NET6_0_OR_GREATER - [MethodImpl(MethodImplOptions.NoInlining)] -#endif - [DoesNotReturn] - public static void ArgumentNullException(string paramName) - => throw new ArgumentNullException(paramName); - - /// - /// Throws an . - /// - /// The name of the parameter that caused the exception. - /// A message that describes the error. -#if !NET6_0_OR_GREATER - [MethodImpl(MethodImplOptions.NoInlining)] -#endif - [DoesNotReturn] - public static void ArgumentNullException(string paramName, string? message) - => throw new ArgumentNullException(paramName, message); - - /// - /// Throws an . - /// - /// The name of the parameter that caused the exception. -#if !NET6_0_OR_GREATER - [MethodImpl(MethodImplOptions.NoInlining)] -#endif - [DoesNotReturn] - public static void ArgumentOutOfRangeException(string paramName) - => throw new ArgumentOutOfRangeException(paramName); - - /// - /// Throws an . - /// - /// The name of the parameter that caused the exception. - /// A message that describes the error. -#if !NET6_0_OR_GREATER - [MethodImpl(MethodImplOptions.NoInlining)] -#endif - [DoesNotReturn] - public static void ArgumentOutOfRangeException(string paramName, string? message) - => throw new ArgumentOutOfRangeException(paramName, message); - - /// - /// Throws an . - /// - /// The name of the parameter that caused the exception. - /// The value of the argument that caused this exception. - /// A message that describes the error. -#if !NET6_0_OR_GREATER - [MethodImpl(MethodImplOptions.NoInlining)] -#endif - [DoesNotReturn] - public static void ArgumentOutOfRangeException(string paramName, object? actualValue, string? message) - => throw new ArgumentOutOfRangeException(paramName, actualValue, message); - - /// - /// Throws an . - /// - /// The name of the parameter that caused the exception. - /// A message that describes the error. -#if !NET6_0_OR_GREATER - [MethodImpl(MethodImplOptions.NoInlining)] -#endif - [DoesNotReturn] - public static void ArgumentException(string paramName, string? message) - => throw new ArgumentException(message, paramName); - - /// - /// Throws an . - /// - /// The name of the parameter that caused the exception. - /// A message that describes the error. - /// The exception that is the cause of the current exception. - /// - /// If the is not a , the current exception is raised in a catch - /// block that handles the inner exception. - /// -#if !NET6_0_OR_GREATER - [MethodImpl(MethodImplOptions.NoInlining)] -#endif - [DoesNotReturn] - public static void ArgumentException(string paramName, string? message, Exception? innerException) - => throw new ArgumentException(message, paramName, innerException); - - /// - /// Throws an . - /// - /// A message that describes the error. -#if !NET6_0_OR_GREATER - [MethodImpl(MethodImplOptions.NoInlining)] -#endif - [DoesNotReturn] - public static void InvalidOperationException(string message) - => throw new InvalidOperationException(message); - - /// - /// Throws an . - /// - /// A message that describes the error. - /// The exception that is the cause of the current exception. -#if !NET6_0_OR_GREATER - [MethodImpl(MethodImplOptions.NoInlining)] -#endif - [DoesNotReturn] - public static void InvalidOperationException(string message, Exception? innerException) - => throw new InvalidOperationException(message, innerException); - - #endregion - - #region For Integer - - /// - /// Throws an if the specified number is less than min. - /// - /// Number to be expected being less than min. - /// The number that must be less than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IfLessThan(int argument, int min, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument < min) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument less than minimum value {min}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is greater than max. - /// - /// Number to be expected being greater than max. - /// The number that must be greater than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IfGreaterThan(int argument, int max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument > max) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument greater than maximum value {max}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is less or equal than min. - /// - /// Number to be expected being less or equal than min. - /// The number that must be less or equal than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IfLessThanOrEqual(int argument, int min, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument <= min) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument less or equal than minimum value {min}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is greater or equal than max. - /// - /// Number to be expected being greater or equal than max. - /// The number that must be greater or equal than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IfGreaterThanOrEqual(int argument, int max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument >= max) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument greater or equal than maximum value {max}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is not in the specified range. - /// - /// Number to be expected being greater or equal than max. - /// The lower bound of the allowed range of argument values. - /// The upper bound of the allowed range of argument values. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IfOutOfRange(int argument, int min, int max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument < min || argument > max) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument not in the range [{min}..{max}]"); - } - - return argument; - } - - /// - /// Throws an if the specified number is equal to 0. - /// - /// Number to be expected being not equal to zero. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IfZero(int argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument == 0) - { - ArgumentOutOfRangeException(paramName, "Argument is zero"); - } - - return argument; - } - - #endregion - - #region For Unsigned Integer - - /// - /// Throws an if the specified number is less than min. - /// - /// Number to be expected being less than min. - /// The number that must be less than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint IfLessThan(uint argument, uint min, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument < min) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument less than minimum value {min}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is greater than max. - /// - /// Number to be expected being greater than max. - /// The number that must be greater than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint IfGreaterThan(uint argument, uint max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument > max) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument greater than maximum value {max}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is less or equal than min. - /// - /// Number to be expected being less or equal than min. - /// The number that must be less or equal than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint IfLessThanOrEqual(uint argument, uint min, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument <= min) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument less or equal than minimum value {min}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is greater or equal than max. - /// - /// Number to be expected being greater or equal than max. - /// The number that must be greater or equal than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint IfGreaterThanOrEqual(uint argument, uint max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument >= max) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument greater or equal than maximum value {max}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is not in the specified range. - /// - /// Number to be expected being greater or equal than max. - /// The lower bound of the allowed range of argument values. - /// The upper bound of the allowed range of argument values. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint IfOutOfRange(uint argument, uint min, uint max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument < min || argument > max) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument not in the range [{min}..{max}]"); - } - - return argument; - } - - /// - /// Throws an if the specified number is equal to 0. - /// - /// Number to be expected being not equal to zero. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static uint IfZero(uint argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument == 0U) - { - ArgumentOutOfRangeException(paramName, "Argument is zero"); - } - - return argument; - } - - #endregion - - #region For Long - - /// - /// Throws an if the specified number is less than min. - /// - /// Number to be expected being less than min. - /// The number that must be less than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long IfLessThan(long argument, long min, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument < min) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument less than minimum value {min}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is greater than max. - /// - /// Number to be expected being greater than max. - /// The number that must be greater than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long IfGreaterThan(long argument, long max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument > max) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument greater than maximum value {max}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is less or equal than min. - /// - /// Number to be expected being less or equal than min. - /// The number that must be less or equal than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long IfLessThanOrEqual(long argument, long min, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument <= min) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument less or equal than minimum value {min}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is greater or equal than max. - /// - /// Number to be expected being greater or equal than max. - /// The number that must be greater or equal than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long IfGreaterThanOrEqual(long argument, long max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument >= max) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument greater or equal than maximum value {max}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is not in the specified range. - /// - /// Number to be expected being greater or equal than max. - /// The lower bound of the allowed range of argument values. - /// The upper bound of the allowed range of argument values. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long IfOutOfRange(long argument, long min, long max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument < min || argument > max) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument not in the range [{min}..{max}]"); - } - - return argument; - } - - /// - /// Throws an if the specified number is equal to 0. - /// - /// Number to be expected being not equal to zero. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static long IfZero(long argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument == 0L) - { - ArgumentOutOfRangeException(paramName, "Argument is zero"); - } - - return argument; - } - - #endregion - - #region For Unsigned Long - - /// - /// Throws an if the specified number is less than min. - /// - /// Number to be expected being less than min. - /// The number that must be less than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong IfLessThan(ulong argument, ulong min, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument < min) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument less than minimum value {min}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is greater than max. - /// - /// Number to be expected being greater than max. - /// The number that must be greater than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong IfGreaterThan(ulong argument, ulong max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument > max) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument greater than maximum value {max}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is less or equal than min. - /// - /// Number to be expected being less or equal than min. - /// The number that must be less or equal than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong IfLessThanOrEqual(ulong argument, ulong min, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument <= min) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument less or equal than minimum value {min}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is greater or equal than max. - /// - /// Number to be expected being greater or equal than max. - /// The number that must be greater or equal than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong IfGreaterThanOrEqual(ulong argument, ulong max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument >= max) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument greater or equal than maximum value {max}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is not in the specified range. - /// - /// Number to be expected being greater or equal than max. - /// The lower bound of the allowed range of argument values. - /// The upper bound of the allowed range of argument values. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong IfOutOfRange(ulong argument, ulong min, ulong max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument < min || argument > max) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument not in the range [{min}..{max}]"); - } - - return argument; - } - - /// - /// Throws an if the specified number is equal to 0. - /// - /// Number to be expected being not equal to zero. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ulong IfZero(ulong argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - if (argument == 0UL) - { - ArgumentOutOfRangeException(paramName, "Argument is zero"); - } - - return argument; - } - - #endregion - - #region For Double - - /// - /// Throws an if the specified number is less than min. - /// - /// Number to be expected being less than min. - /// The number that must be less than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double IfLessThan(double argument, double min, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - // strange conditional needed in order to handle NaN values correctly -#pragma warning disable S1940 // Boolean checks should not be inverted - if (!(argument >= min)) -#pragma warning restore S1940 // Boolean checks should not be inverted - { - ArgumentOutOfRangeException(paramName, argument, $"Argument less than minimum value {min}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is greater than max. - /// - /// Number to be expected being greater than max. - /// The number that must be greater than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double IfGreaterThan(double argument, double max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - // strange conditional needed in order to handle NaN values correctly -#pragma warning disable S1940 // Boolean checks should not be inverted - if (!(argument <= max)) -#pragma warning restore S1940 // Boolean checks should not be inverted - { - ArgumentOutOfRangeException(paramName, argument, $"Argument greater than maximum value {max}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is less or equal than min. - /// - /// Number to be expected being less or equal than min. - /// The number that must be less or equal than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double IfLessThanOrEqual(double argument, double min, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - // strange conditional needed in order to handle NaN values correctly -#pragma warning disable S1940 // Boolean checks should not be inverted - if (!(argument > min)) -#pragma warning restore S1940 // Boolean checks should not be inverted - { - ArgumentOutOfRangeException(paramName, argument, $"Argument less or equal than minimum value {min}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is greater or equal than max. - /// - /// Number to be expected being greater or equal than max. - /// The number that must be greater or equal than the argument. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double IfGreaterThanOrEqual(double argument, double max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - // strange conditional needed in order to handle NaN values correctly -#pragma warning disable S1940 // Boolean checks should not be inverted - if (!(argument < max)) -#pragma warning restore S1940 // Boolean checks should not be inverted - { - ArgumentOutOfRangeException(paramName, argument, $"Argument greater or equal than maximum value {max}"); - } - - return argument; - } - - /// - /// Throws an if the specified number is not in the specified range. - /// - /// Number to be expected being greater or equal than max. - /// The lower bound of the allowed range of argument values. - /// The upper bound of the allowed range of argument values. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double IfOutOfRange(double argument, double min, double max, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { - // strange conditional needed in order to handle NaN values correctly - if (!(min <= argument && argument <= max)) - { - ArgumentOutOfRangeException(paramName, argument, $"Argument not in the range [{min}..{max}]"); - } - - return argument; - } - - /// - /// Throws an if the specified number is equal to 0. - /// - /// Number to be expected being not equal to zero. - /// The name of the parameter being checked. - /// The original value of . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double IfZero(double argument, [CallerArgumentExpression(nameof(argument))] string paramName = "") - { -#pragma warning disable S1244 // Floating point numbers should not be tested for equality - if (argument == 0.0) -#pragma warning restore S1244 // Floating point numbers should not be tested for equality - { - ArgumentOutOfRangeException(paramName, "Argument is zero"); - } - - return argument; - } - - #endregion -} diff --git a/src/Agents/Extensions/YamlConfigurationStreamParser.cs b/src/Agents/Extensions/YamlConfigurationStreamParser.cs deleted file mode 100644 index 38d262a..0000000 --- a/src/Agents/Extensions/YamlConfigurationStreamParser.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Microsoft.Extensions.Configuration; -using YamlDotNet.RepresentationModel; - -namespace NetEscapades.Configuration.Yaml -{ - internal class YamlConfigurationStreamParser - { - private readonly IDictionary _data = new SortedDictionary(StringComparer.OrdinalIgnoreCase); - private readonly Stack _context = new Stack(); - private string _currentPath; - - public IDictionary Parse(Stream input) - { - _data.Clear(); - _context.Clear(); - - // https://dotnetfiddle.net/rrR2Bb - var yaml = new YamlStream(); - yaml.Load(new StreamReader(input, detectEncodingFromByteOrderMarks: true)); - - if (yaml.Documents.Any()) - { - var mapping = (YamlMappingNode)yaml.Documents[0].RootNode; - - // The document node is a mapping node - VisitYamlMappingNode(mapping); - } - - return _data; - } - - private void VisitYamlNodePair(KeyValuePair yamlNodePair) - { - var context = ((YamlScalarNode)yamlNodePair.Key).Value; - VisitYamlNode(context, yamlNodePair.Value); - } - - private void VisitYamlNode(string context, YamlNode node) - { - if (node is YamlScalarNode scalarNode) - { - VisitYamlScalarNode(context, scalarNode); - } - if (node is YamlMappingNode mappingNode) - { - VisitYamlMappingNode(context, mappingNode); - } - if (node is YamlSequenceNode sequenceNode) - { - VisitYamlSequenceNode(context, sequenceNode); - } - } - - private void VisitYamlScalarNode(string context, YamlScalarNode yamlValue) - { - //a node with a single 1-1 mapping - EnterContext(context); - var currentKey = _currentPath; - - if (_data.ContainsKey(currentKey)) - { - throw new FormatException(Resources.FormatError_KeyIsDuplicated(currentKey)); - } - - _data[currentKey] = IsNullValue(yamlValue) ? null : yamlValue.Value; - ExitContext(); - } - - private void VisitYamlMappingNode(YamlMappingNode node) - { - foreach (var yamlNodePair in node.Children) - { - VisitYamlNodePair(yamlNodePair); - } - } - - private void VisitYamlMappingNode(string context, YamlMappingNode yamlValue) - { - //a node with an associated sub-document - EnterContext(context); - - VisitYamlMappingNode(yamlValue); - - ExitContext(); - } - - private void VisitYamlSequenceNode(string context, YamlSequenceNode yamlValue) - { - //a node with an associated list - EnterContext(context); - - VisitYamlSequenceNode(yamlValue); - - ExitContext(); - } - - private void VisitYamlSequenceNode(YamlSequenceNode node) - { - for (int i = 0; i < node.Children.Count; i++) - { - VisitYamlNode(i.ToString(), node.Children[i]); - } - } - - private void EnterContext(string context) - { - _context.Push(context); - _currentPath = ConfigurationPath.Combine(_context.Reverse()); - } - - private void ExitContext() - { - _context.Pop(); - _currentPath = ConfigurationPath.Combine(_context.Reverse()); - } - - private bool IsNullValue(YamlScalarNode yamlValue) - { - return yamlValue.Style == YamlDotNet.Core.ScalarStyle.Plain - && ( - yamlValue.Value == "~" - || yamlValue.Value == "null" - || yamlValue.Value == "Null" - || yamlValue.Value == "NULL" - ); - } - } -} diff --git a/src/Agents/IAIAgentsBuilder.cs b/src/Agents/IAIAgentsBuilder.cs deleted file mode 100644 index c44d02c..0000000 --- a/src/Agents/IAIAgentsBuilder.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Diagnostics.Metrics; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; - -namespace Devlooped.Agents.AI; - -/// Provides a mechanism to configure AI agents. -public interface IAIAgentsBuilder : IHostApplicationBuilder -{ -} - -class DefaultAIAgentsBuilder(IHostApplicationBuilder builder) : IAIAgentsBuilder -{ - public IDictionary Properties => builder.Properties; - - public IConfigurationManager Configuration => builder.Configuration; - - public IHostEnvironment Environment => builder.Environment; - - public ILoggingBuilder Logging => builder.Logging; - - public IMetricsBuilder Metrics => builder.Metrics; - - public IServiceCollection Services => builder.Services; - - public void ConfigureContainer(IServiceProviderFactory factory, Action? configure = null) where TContainerBuilder : notnull => builder.ConfigureContainer(factory, configure); -} \ No newline at end of file diff --git a/src/Agents/IHasAdditionalProperties.cs b/src/Agents/IHasAdditionalProperties.cs deleted file mode 100644 index 4b30884..0000000 --- a/src/Agents/IHasAdditionalProperties.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Microsoft.Extensions.AI; - -namespace Devlooped.Agents.AI; - -/// Indicates that the instance can have additional properties associated with it. -public interface IHasAdditionalProperties -{ - /// Gets or sets any additional properties associated with the instance. - AdditionalPropertiesDictionary? AdditionalProperties { get; set; } -} diff --git a/src/Agents/StaticAIContextProvider.cs b/src/Agents/StaticAIContextProvider.cs deleted file mode 100644 index 4c75e2b..0000000 --- a/src/Agents/StaticAIContextProvider.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Microsoft.Agents.AI; - -namespace Devlooped.Agents.AI; - -class StaticAIContextProvider(AIContext context) : AIContextProvider -{ - public AIContext Context => context; - - public override ValueTask InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default) - => ValueTask.FromResult(Context); -} diff --git a/src/Agents/readme.md b/src/Agents/readme.md deleted file mode 100644 index a3c40ed..0000000 --- a/src/Agents/readme.md +++ /dev/null @@ -1,9 +0,0 @@ -[![EULA](https://img.shields.io/badge/EULA-OSMF-blue?labelColor=black&color=C9FF30)](osmfeula.txt) -[![OSS](https://img.shields.io/github/license/devlooped/oss.svg?color=blue)](license.txt) -[![GitHub](https://img.shields.io/badge/-source-181717.svg?logo=GitHub)](https://github.com/devlooped/AI) - - - - - - \ No newline at end of file