| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
3148bcebf2 | update oai and aoai package version | 1 year ago |
|
|
e5810a3920 | Merge branch 'LittleLittleCloud-patch-1' of https://github.com/microsoft/autogen into LittleLittleCloud-patch-1 | 1 year ago |
|
|
297c1f08fa | fix aot tests | 1 year ago |
|
|
cdaa9b00ad
|
Update dotnet-build.yml | 1 year ago |
|
|
d834d257b5 | fix formatting | 1 year ago |
|
|
1ffb7dc935 | fix whitespace | 1 year ago |
|
|
32c0605a7a | fix IDE warning | 1 year ago |
|
|
0245ab7321
|
Update rollForward to latestFeature in global.json | 1 year ago |
|
|
f20d326942
|
Update dotnet-build.yml | 1 year ago |
|
|
1d4f31041c
|
.NET update global.json to use 9.0.100 | 1 year ago |
| @@ -81,6 +81,10 @@ jobs: | |||
| uses: actions/setup-dotnet@v4 | |||
| with: | |||
| dotnet-version: '8.0.x' | |||
| - name: Setup .NET 9.0 | |||
| uses: actions/setup-dotnet@v4 | |||
| with: | |||
| dotnet-version: '9.0.x' | |||
| - name: Restore dependencies | |||
| run: dotnet restore -bl | |||
| - name: Format check | |||
| @@ -207,6 +211,10 @@ jobs: | |||
| uses: actions/setup-dotnet@v4 | |||
| with: | |||
| dotnet-version: '8.0.x' | |||
| - name: Setup .NET 9.0 | |||
| uses: actions/setup-dotnet@v4 | |||
| with: | |||
| dotnet-version: '9.0.x' | |||
| - name: publish AOT testApp, assert static analysis warning count, and run the app | |||
| shell: pwsh | |||
| @@ -249,6 +257,10 @@ jobs: | |||
| with: | |||
| dotnet-version: '8.0.x' | |||
| global-json-file: dotnet/global.json | |||
| - name: Setup .NET 9.0 | |||
| uses: actions/setup-dotnet@v4 | |||
| with: | |||
| dotnet-version: '9.0.x' | |||
| - name: Install dev certs | |||
| run: dotnet --version && dotnet dev-certs https --trust | |||
| - name: Restore dependencies | |||
| @@ -15,7 +15,7 @@ foreach ($line in $($publishOutput -split "`r`n")) | |||
| } | |||
| } | |||
| pushd $rootDirectory/artifacts/bin/AutoGen.AotCompatibility.Tests/release | |||
| pushd $rootDirectory/artifacts/bin/AutoGen.AotCompatibility.Tests/release/native | |||
| Write-Host "Executing test App..." | |||
| ./AutoGen.AotCompatibility.Tests | |||
| @@ -2,10 +2,12 @@ | |||
| <PropertyGroup> | |||
| <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> | |||
| <MicrosoftSemanticKernelVersion>1.22.0</MicrosoftSemanticKernelVersion> | |||
| <MicrosoftSemanticKernelExperimentalVersion>1.22.0-alpha</MicrosoftSemanticKernelExperimentalVersion> | |||
| <MicrosoftExtensionsAIVersion>9.0.0-preview.9.24525.1</MicrosoftExtensionsAIVersion> | |||
| <MicrosoftSemanticKernelStableVersion>1.45.0</MicrosoftSemanticKernelStableVersion> | |||
| <MicrosoftSemanticKernelPreviewVersion>$(MicrosoftSemanticKernelStableVersion)-preview</MicrosoftSemanticKernelPreviewVersion> | |||
| <MicrosoftSemanticKernelAlphaVersion>$(MicrosoftSemanticKernelStableVersion)-alpha</MicrosoftSemanticKernelAlphaVersion> | |||
| <MicrosoftExtensionsAIVersion>9.3.0-preview.1.25161.3</MicrosoftExtensionsAIVersion> | |||
| <MicrosoftExtensionConfiguration>9.0.0</MicrosoftExtensionConfiguration> | |||
| <MicrosoftExtensionDependencyInjection>9.0.0</MicrosoftExtensionDependencyInjection> | |||
| <MicrosoftExtensionDependencyInjection>9.0.3</MicrosoftExtensionDependencyInjection> | |||
| <MicrosoftExtensionLogging>9.0.0</MicrosoftExtensionLogging> | |||
| <MicrosoftExtensionOptions>9.0.0</MicrosoftExtensionOptions> | |||
| <MicrosoftOrleans>9.0.1</MicrosoftOrleans> | |||
| @@ -18,7 +20,7 @@ | |||
| <PackageVersion Include="Aspire.Hosting" Version="9.0.0" /> | |||
| <PackageVersion Include="Aspire.Hosting.Python" Version="9.0.0" /> | |||
| <PackageVersion Include="Aspire.Hosting.Testing" Version="9.0.0" /> | |||
| <PackageVersion Include="Aspire.Azure.AI.OpenAI" Version="8.0.1-preview.8.24267.1" /> | |||
| <PackageVersion Include="Aspire.Azure.AI.OpenAI" Version="9.0.0-preview.5.24551.3" /> | |||
| <PackageVersion Include="Aspire.Hosting.AppHost" Version="9.0.0" /> | |||
| <PackageVersion Include="Aspire.Hosting.Azure.ApplicationInsights" Version="9.0.0" /> | |||
| <PackageVersion Include="Aspire.Hosting.Azure.CognitiveServices" Version="9.0.0" /> | |||
| @@ -27,9 +29,10 @@ | |||
| <PackageVersion Include="Aspire.Hosting.Qdrant" Version="9.0.0" /> | |||
| <PackageVersion Include="Aspire.Hosting.Redis" Version="8.2.0" /> | |||
| <PackageVersion Include="AspNetCore.Authentication.ApiKey" Version="8.0.1" /> | |||
| <PackageVersion Include="Azure.AI.OpenAI" Version="2.1.0-beta.2" /> | |||
| <PackageVersion Include="Azure.AI.OpenAI" Version="2.2.0-beta.4" /> | |||
| <PackageVersion Include="Azure.AI.Inference" Version="1.0.0-beta.1" /> | |||
| <PackageVersion Include="Azure.Data.Tables" Version="12.9.1" /> | |||
| <PackageVersion Include="Azure.Core" Version="1.44.1" /> | |||
| <PackageVersion Include="Azure.Identity" Version="1.13.1" /> | |||
| <PackageVersion Include="Azure.ResourceManager.ContainerInstance" Version="1.2.1" /> | |||
| <PackageVersion Include="Azure.Storage.Files.Shares" Version="12.21.0" /> | |||
| @@ -100,18 +103,18 @@ | |||
| <PackageVersion Include="Microsoft.Orleans.Streaming.EventHubs" Version="$(MicrosoftOrleans)" /> | |||
| <PackageVersion Include="Microsoft.Orleans.TestingHost" Version="9.0.1" /> | |||
| <PackageVersion Include="Microsoft.PowerShell.SDK" Version="7.5.0" /> | |||
| <PackageVersion Include="Microsoft.SemanticKernel" Version="1.29.0" /> | |||
| <PackageVersion Include="Microsoft.SemanticKernel.Agents.Core" Version="$(MicrosoftSemanticKernelExperimentalVersion)" /> | |||
| <PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureOpenAI" Version="1.29.0" /> | |||
| <PackageVersion Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="$(MicrosoftSemanticKernelExperimentalVersion)" /> | |||
| <PackageVersion Include="Microsoft.SemanticKernel.Plugins.Memory" Version="$(MicrosoftSemanticKernelExperimentalVersion)" /> | |||
| <PackageVersion Include="Microsoft.SemanticKernel.Plugins.Web" Version="$(MicrosoftSemanticKernelExperimentalVersion)" /> | |||
| <PackageVersion Include="Microsoft.SemanticKernel" Version="$(MicrosoftSemanticKernelStableVersion)" /> | |||
| <PackageVersion Include="Microsoft.SemanticKernel.Agents.Core" Version="$(MicrosoftSemanticKernelStableVersion)" /> | |||
| <PackageVersion Include="Microsoft.SemanticKernel.Connectors.AzureOpenAI" Version="$(MicrosoftSemanticKernelStableVersion)" /> | |||
| <PackageVersion Include="Microsoft.SemanticKernel.Connectors.Qdrant" Version="$(MicrosoftSemanticKernelPreviewVersion)" /> | |||
| <PackageVersion Include="Microsoft.SemanticKernel.Plugins.Memory" Version="$(MicrosoftSemanticKernelAlphaVersion)" /> | |||
| <PackageVersion Include="Microsoft.SemanticKernel.Plugins.Web" Version="$(MicrosoftSemanticKernelAlphaVersion)" /> | |||
| <PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" /> | |||
| <PackageVersion Include="Moq" Version="4.20.72" /> | |||
| <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" /> | |||
| <PackageVersion Include="Octokit" Version="13.0.1" /> | |||
| <PackageVersion Include="Octokit.Webhooks.AspNetCore" Version="2.4.1" /> | |||
| <PackageVersion Include="OpenAI" Version="2.1.0-beta.2" /> | |||
| <PackageVersion Include="OpenAI" Version="2.2.0-beta.4" /> | |||
| <PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.10.0" /> | |||
| <PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.10.0" /> | |||
| <PackageVersion Include="OpenTelemetry.Instrumentation.AspNetCore" Version="$(OpenTelemetryInstrumentation)" /> | |||
| @@ -127,7 +130,7 @@ | |||
| <PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.2.1" /> | |||
| <PackageVersion Include="System.IO.Packaging" Version="9.0.0" /> | |||
| <PackageVersion Include="System.Memory.Data" Version="9.0.0" /> | |||
| <PackageVersion Include="System.Text.Json" Version="9.0.0" /> | |||
| <PackageVersion Include="System.Text.Json" Version="9.0.3" /> | |||
| <PackageVersion Include="xunit" Version="2.9.2" /> | |||
| <PackageVersion Include="xunit.runner.console" Version="2.9.2" /> | |||
| <PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" /> | |||
| @@ -1,6 +1,6 @@ | |||
| { | |||
| "sdk": { | |||
| "version": "8.0.401", | |||
| "rollForward": "latestMinor" | |||
| "version": "9.0.100", | |||
| "rollForward": "latestFeature" | |||
| } | |||
| } | |||
| @@ -42,8 +42,6 @@ public class Tool_Call_With_Ollama_And_LiteLLM | |||
| }); | |||
| #endregion Create_tools | |||
| #region Create_Agent | |||
| var liteLLMUrl = "http://localhost:4000"; | |||
| // api-key is not required for local server | |||
| // so you can use any string here | |||
| var openAIClient = new OpenAIClient(new ApiKeyCredential("api-key"), new OpenAIClientOptions | |||
| @@ -59,7 +57,7 @@ public class Tool_Call_With_Ollama_And_LiteLLM | |||
| .RegisterMiddleware(functionMiddleware) | |||
| .RegisterPrintMessage(); | |||
| var reply = await agent.SendAsync("what's the weather in new york"); | |||
| await agent.SendAsync("what's the weather in new york"); | |||
| #endregion Create_Agent | |||
| } | |||
| } | |||
| @@ -40,6 +40,6 @@ public class DummyAgent : IStreamingAgent | |||
| foreach (var c in reply) | |||
| { | |||
| yield return new TextMessageUpdate(Role.Assistant, c.ToString(), this.Name); | |||
| }; | |||
| } | |||
| } | |||
| } | |||
| @@ -4,6 +4,8 @@ | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Reflection; | |||
| using System.Text.Json; | |||
| using System.Text.Json.Serialization; | |||
| using Microsoft.Extensions.AI; | |||
| @@ -69,36 +71,48 @@ public class FunctionContract | |||
| /// </summary> | |||
| public string? ReturnDescription { get; set; } | |||
| public static implicit operator FunctionContract(AIFunctionMetadata metadata) | |||
| public static implicit operator FunctionContract(AIFunction function) | |||
| { | |||
| return new FunctionContract | |||
| var openapiScheme = function.JsonSchema; | |||
| var parameters = new List<FunctionParameterContract>(); | |||
| string[] isRequiredProperties = []; | |||
| if (openapiScheme.TryGetProperty("required", out var requiredElement)) | |||
| { | |||
| Namespace = metadata.AdditionalProperties.ContainsKey(NamespaceKey) ? metadata.AdditionalProperties[NamespaceKey] as string : null, | |||
| ClassName = metadata.AdditionalProperties.ContainsKey(ClassNameKey) ? metadata.AdditionalProperties[ClassNameKey] as string : null, | |||
| Name = metadata.Name, | |||
| Description = metadata.Description, | |||
| Parameters = metadata.Parameters?.Select(p => (FunctionParameterContract)p).ToList(), | |||
| ReturnType = metadata.ReturnParameter.ParameterType, | |||
| ReturnDescription = metadata.ReturnParameter.Description, | |||
| }; | |||
| } | |||
| isRequiredProperties = requiredElement.Deserialize<string[]>() ?? []; | |||
| } | |||
| public static implicit operator AIFunctionMetadata(FunctionContract contract) | |||
| { | |||
| return new AIFunctionMetadata(contract.Name) | |||
| var parameterList = function.UnderlyingMethod?.GetParameters() ?? Array.Empty<ParameterInfo>(); | |||
| if (openapiScheme.TryGetProperty("properties", out var propertiesElement)) | |||
| { | |||
| Description = contract.Description, | |||
| ReturnParameter = new AIFunctionReturnParameterMetadata() | |||
| var properties = propertiesElement.Deserialize<Dictionary<string, JsonElement>>() ?? new Dictionary<string, JsonElement>(); | |||
| foreach (var property in properties) | |||
| { | |||
| Description = contract.ReturnDescription, | |||
| ParameterType = contract.ReturnType, | |||
| }, | |||
| AdditionalProperties = new Dictionary<string, object?> | |||
| { | |||
| [NamespaceKey] = contract.Namespace, | |||
| [ClassNameKey] = contract.ClassName, | |||
| }, | |||
| Parameters = [.. contract.Parameters?.Select(p => (AIFunctionParameterMetadata)p)!], | |||
| var parameterType = parameterList.FirstOrDefault(p => p.Name == property.Key)?.ParameterType; | |||
| var parameter = new FunctionParameterContract | |||
| { | |||
| Name = property.Key, | |||
| ParameterType = parameterType, // TODO: Need to get the type from the schema | |||
| IsRequired = isRequiredProperties.Contains(property.Key), | |||
| }; | |||
| if (property.Value.TryGetProperty("description", out var descriptionElement)) | |||
| { | |||
| parameter.Description = descriptionElement.GetString(); | |||
| } | |||
| if (property.Value.TryGetProperty("default", out var defaultValueElement)) | |||
| { | |||
| parameter.DefaultValue = defaultValueElement.Deserialize<object>(); | |||
| } | |||
| parameters.Add(parameter); | |||
| } | |||
| } | |||
| return new FunctionContract | |||
| { | |||
| Namespace = function.AdditionalProperties.ContainsKey(NamespaceKey) ? function.AdditionalProperties[NamespaceKey] as string : null, | |||
| ClassName = function.AdditionalProperties.ContainsKey(ClassNameKey) ? function.AdditionalProperties[ClassNameKey] as string : null, | |||
| Name = function.Name, | |||
| Description = function.Description, | |||
| Parameters = parameters, | |||
| }; | |||
| } | |||
| } | |||
| @@ -132,29 +146,4 @@ public class FunctionParameterContract | |||
| /// The default value of the parameter. | |||
| /// </summary> | |||
| public object? DefaultValue { get; set; } | |||
| // convert to/from FunctionParameterMetadata | |||
| public static implicit operator FunctionParameterContract(AIFunctionParameterMetadata metadata) | |||
| { | |||
| return new FunctionParameterContract | |||
| { | |||
| Name = metadata.Name, | |||
| Description = metadata.Description, | |||
| ParameterType = metadata.ParameterType, | |||
| IsRequired = metadata.IsRequired, | |||
| DefaultValue = metadata.DefaultValue, | |||
| }; | |||
| } | |||
| public static implicit operator AIFunctionParameterMetadata(FunctionParameterContract contract) | |||
| { | |||
| return new AIFunctionParameterMetadata(contract.Name!) | |||
| { | |||
| DefaultValue = contract.DefaultValue, | |||
| Description = contract.Description, | |||
| IsRequired = contract.IsRequired, | |||
| ParameterType = contract.ParameterType, | |||
| HasDefaultValue = contract.DefaultValue != null, | |||
| }; | |||
| } | |||
| } | |||
| @@ -53,9 +53,9 @@ public class FunctionCallMiddleware : IStreamingMiddleware | |||
| public FunctionCallMiddleware(IEnumerable<AIFunction> functions, string? name = null) | |||
| { | |||
| this.Name = name ?? nameof(FunctionCallMiddleware); | |||
| this.functions = functions.Select(f => (FunctionContract)f.Metadata).ToArray(); | |||
| this.functions = functions.Select(f => (FunctionContract)f).ToArray(); | |||
| this.functionMap = functions.Select(f => (f.Metadata.Name, this.AIToolInvokeWrapper(f.InvokeAsync))).ToDictionary(f => f.Name, f => f.Item2); | |||
| this.functionMap = functions.Select(f => (f.Name, this.AIToolInvokeWrapper(f.InvokeAsync))).ToDictionary(f => f.Name, f => f.Item2); | |||
| } | |||
| public string? Name { get; } | |||
| @@ -26,8 +26,9 @@ public class SemanticKernelChatCompletionAgent : IAgent | |||
| public async Task<IMessage> GenerateReplyAsync(IEnumerable<IMessage> messages, GenerateReplyOptions? options = null, | |||
| CancellationToken cancellationToken = default) | |||
| { | |||
| ChatMessageContent[] reply = await _chatCompletionAgent | |||
| .InvokeAsync(BuildChatHistory(messages), cancellationToken: cancellationToken) | |||
| var agentThread = new ChatHistoryAgentThread(BuildChatHistory(messages)); | |||
| var reply = await _chatCompletionAgent | |||
| .InvokeAsync(agentThread, cancellationToken: cancellationToken) | |||
| .ToArrayAsync(cancellationToken: cancellationToken); | |||
| return reply.Length > 1 | |||
| @@ -170,24 +170,23 @@ public interface IChatAgent : | |||
| /// <summary> | |||
| /// The name of the agent. This is used by team to uniquely identify the agent.It should be unique within the team. | |||
| /// </summary> | |||
| AgentName Name { get; } | |||
| public AgentName Name { get; } | |||
| /// <summary> | |||
| /// The description of the agent. This is used by team to make decisions about which agents to use.The description | |||
| /// should describe the agent's capabilities and how to interact with it. | |||
| /// </summary> | |||
| string Description { get; } | |||
| public string Description { get; } | |||
| /// <summary> | |||
| /// The types of messages that the agent produces. | |||
| /// </summary> | |||
| IEnumerable<Type> ProducedMessageTypes { get; } // TODO: Is there a way to make this part of the type somehow? | |||
| // Annotations, or IProduce<>? Do we ever actually access this? | |||
| public IEnumerable<Type> ProducedMessageTypes { get; } // TODO: Is there a way to make this part of the type somehow? Annotations, or IProduce<>? Do we ever actually access this? | |||
| /// <summary> | |||
| /// Reset the agent to its initialization state. | |||
| /// </summary> | |||
| /// <param name="cancellationToken"></param> | |||
| /// <returns></returns> | |||
| ValueTask ResetAsync(CancellationToken cancellationToken); | |||
| public ValueTask ResetAsync(CancellationToken cancellationToken); | |||
| } | |||
| @@ -5,22 +5,22 @@ namespace Microsoft.AutoGen.AgentChat.Abstractions; | |||
| public interface IHandleChat<in TIn> | |||
| { | |||
| ValueTask HandleAsync(TIn item) | |||
| public ValueTask HandleAsync(TIn item) | |||
| { | |||
| return this.HandleAsync(item, CancellationToken.None); | |||
| } | |||
| ValueTask HandleAsync(TIn item, CancellationToken cancellationToken); | |||
| public ValueTask HandleAsync(TIn item, CancellationToken cancellationToken); | |||
| } | |||
| public interface IHandleChat<in TIn, TOut> // TODO: Map this to IHandle<> somehow? | |||
| { | |||
| ValueTask<TOut> HandleAsync(TIn item) | |||
| public ValueTask<TOut> HandleAsync(TIn item) | |||
| { | |||
| return this.HandleAsync(item, CancellationToken.None); | |||
| } | |||
| ValueTask<TOut> HandleAsync(TIn item, CancellationToken cancellationToken); | |||
| public ValueTask<TOut> HandleAsync(TIn item, CancellationToken cancellationToken); | |||
| } | |||
| public interface IHandleDefault : IHandleChat<object> | |||
| @@ -29,10 +29,10 @@ public interface IHandleDefault : IHandleChat<object> | |||
| public interface IHandleStream<in TIn, TOut> | |||
| { | |||
| IAsyncEnumerable<TOut> StreamAsync(TIn item) | |||
| public IAsyncEnumerable<TOut> StreamAsync(TIn item) | |||
| { | |||
| return this.StreamAsync(item, CancellationToken.None); | |||
| } | |||
| IAsyncEnumerable<TOut> StreamAsync(TIn item, CancellationToken cancellationToken); | |||
| public IAsyncEnumerable<TOut> StreamAsync(TIn item, CancellationToken cancellationToken); | |||
| } | |||
| @@ -116,7 +116,7 @@ public struct MultiModalData | |||
| /// <param name="item">The <see cref="AIContent"/> to wrap.</param> | |||
| /// <returns>A <see cref="MultiModalData"/> instance wrapping the <paramref name="item"/>.</returns> | |||
| /// <exception cref="ArgumentException"> | |||
| /// Thrown if the <paramref name="item"/> is not a <see cref="TextContent"/> or <see cref="ImageContent"/>. | |||
| /// Thrown if the <paramref name="item"/> is not a <see cref="TextContent"/> or <see cref="DataContent"/>. | |||
| /// </exception> | |||
| public static MultiModalData CheckTypeAndCreate(AIContent item) | |||
| { | |||
| @@ -124,7 +124,7 @@ public struct MultiModalData | |||
| { | |||
| return new MultiModalData(text); | |||
| } | |||
| else if (item is ImageContent image) | |||
| else if (item is DataContent image) | |||
| { | |||
| return new MultiModalData(image); | |||
| } | |||
| @@ -155,10 +155,10 @@ public struct MultiModalData | |||
| } | |||
| /// <summary> | |||
| /// Initializes a new instance of the <see cref="MultiModalData"/> with an <see cref="ImageContent"/>. | |||
| /// Initializes a new instance of the <see cref="MultiModalData"/> with an <see cref="DataContent"/>. | |||
| /// </summary> | |||
| /// <param name="image">The image to wrap.</param> | |||
| public MultiModalData(ImageContent image) | |||
| public MultiModalData(DataContent image) | |||
| { | |||
| ContentType = Type.Image; | |||
| AIContent = image; | |||
| @@ -246,12 +246,12 @@ public class MultiModalMessage : ChatMessage, IList<AIContent> | |||
| } | |||
| /// <summary> | |||
| /// Adds a range of <see cref="ImageContent"/> to the message. | |||
| /// Adds a range of <see cref="DataContent"/> to the message. | |||
| /// </summary> | |||
| /// <param name="images">The items to add.</param> | |||
| public void AddRange(IEnumerable<ImageContent> images) | |||
| public void AddRange(IEnumerable<DataContent> images) | |||
| { | |||
| foreach (ImageContent image in images) | |||
| foreach (DataContent image in images) | |||
| { | |||
| this.Add(image); | |||
| } | |||
| @@ -279,7 +279,7 @@ public class MultiModalMessage : ChatMessage, IList<AIContent> | |||
| /// Adds a <see cref="TextContent"/> to the message. | |||
| /// </summary> | |||
| /// <param name="image">The image to add.</param> | |||
| public void Add(ImageContent image) | |||
| public void Add(DataContent image) | |||
| { | |||
| this.Content.Add(new(image)); | |||
| } | |||
| @@ -366,7 +366,7 @@ public class MultiModalMessage : ChatMessage, IList<AIContent> | |||
| } | |||
| /// <inheritdoc cref="IList{ImageContent}.Insert(int, ImageContent)"/> | |||
| public void Insert(int index, ImageContent image) | |||
| public void Insert(int index, DataContent image) | |||
| { | |||
| this.Content.Insert(index, new(image)); | |||
| } | |||
| @@ -597,7 +597,7 @@ public static class CompletionChatMessageExtensions | |||
| { | |||
| contentBuilder.AppendLine(textContent.Text); | |||
| } | |||
| else if (content is ImageContent) | |||
| else if (content is DataContent) | |||
| { | |||
| contentBuilder.AppendLine("[Image]"); | |||
| } | |||
| @@ -59,7 +59,7 @@ public interface ITaskRunner | |||
| /// <param name="task">The task definition in text form.</param> | |||
| /// <param name="cancellationToken"></param> | |||
| /// <returns>The result of running the task.</returns> | |||
| async ValueTask<TaskResult> RunAsync(string task, CancellationToken cancellationToken = default) => | |||
| public async ValueTask<TaskResult> RunAsync(string task, CancellationToken cancellationToken = default) => | |||
| await this.RunAsync(ToMessage(task)!, cancellationToken); | |||
| /// <summary> | |||
| @@ -73,7 +73,7 @@ public interface ITaskRunner | |||
| /// <param name="cancellationToken"></param> | |||
| /// <returns>The result of running the task.</returns> | |||
| /// <exception cref="InvalidOperationException">If no response is generated.</exception> | |||
| async ValueTask<TaskResult> RunAsync(ChatMessage task, CancellationToken cancellationToken = default) | |||
| public async ValueTask<TaskResult> RunAsync(ChatMessage task, CancellationToken cancellationToken = default) | |||
| { | |||
| await foreach (TaskFrame frame in this.StreamAsync(task, cancellationToken)) | |||
| { | |||
| @@ -98,7 +98,7 @@ public interface ITaskRunner | |||
| /// <param name="cancellationToken"></param> | |||
| /// <returns>A stream of <see cref="TaskFrame"/> containing internal messages and intermediate results followed by | |||
| /// the final <see cref="TaskResult"/></returns> | |||
| IAsyncEnumerable<TaskFrame> StreamAsync(string task, CancellationToken cancellationToken = default) => | |||
| public IAsyncEnumerable<TaskFrame> StreamAsync(string task, CancellationToken cancellationToken = default) => | |||
| this.StreamAsync(ToMessage(task), cancellationToken); | |||
| /// <summary> | |||
| @@ -113,5 +113,5 @@ public interface ITaskRunner | |||
| /// <param name="cancellationToken"></param> | |||
| /// <returns>A stream of <see cref="TaskFrame"/> containing internal messages and intermediate results followed by | |||
| /// the final <see cref="TaskResult"/></returns> | |||
| IAsyncEnumerable<TaskFrame> StreamAsync(ChatMessage? task, CancellationToken cancellationToken = default); | |||
| public IAsyncEnumerable<TaskFrame> StreamAsync(ChatMessage? task, CancellationToken cancellationToken = default); | |||
| } | |||
| @@ -19,7 +19,7 @@ public interface ITerminationCondition | |||
| /// <summary> | |||
| /// Checks if the termination condition has been reached | |||
| /// </summary> | |||
| bool IsTerminated { get; } | |||
| public bool IsTerminated { get; } | |||
| /// <summary> | |||
| /// Check if the conversation should be terminated based on the messages received | |||
| @@ -30,19 +30,19 @@ public interface ITerminationCondition | |||
| /// <returns>A <see cref="StopMessage"/> if the conversation should be terminated, or <c>null</c> | |||
| /// otherwise.</returns> | |||
| /// <exception cref="TerminatedException">If the termination condition has already been reached.</exception> | |||
| ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages); | |||
| public ValueTask<StopMessage?> CheckAndUpdateAsync(IList<AgentMessage> messages); | |||
| /// <summary> | |||
| /// Resets the termination condition. | |||
| /// </summary> | |||
| void Reset(); | |||
| public void Reset(); | |||
| /// <summary> | |||
| /// Combine this termination condition with another using a logical OR. | |||
| /// </summary> | |||
| /// <param name="other">Another termination condition.</param> | |||
| /// <returns>The combined termination condition, with appropriate short-circuiting.</returns> | |||
| ITerminationCondition Or(ITerminationCondition other) | |||
| public ITerminationCondition Or(ITerminationCondition other) | |||
| { | |||
| return new CombinerCondition(CombinerCondition.Or, this, other); | |||
| } | |||
| @@ -52,7 +52,7 @@ public interface ITerminationCondition | |||
| /// </summary> | |||
| /// <param name="other">Another termination condition.</param> | |||
| /// <returns>The combined termination condition, with appropriate short-circuiting.</returns> | |||
| ITerminationCondition And(ITerminationCondition other) | |||
| public ITerminationCondition And(ITerminationCondition other) | |||
| { | |||
| return new CombinerCondition(CombinerCondition.And, this, other); | |||
| } | |||
| @@ -1,7 +1,6 @@ | |||
| // Copyright (c) Microsoft Corporation. All rights reserved. | |||
| // Tools.cs | |||
| using System.ComponentModel; | |||
| using System.Reflection; | |||
| using Microsoft.Extensions.AI; | |||
| @@ -10,37 +9,6 @@ namespace Microsoft.AutoGen.AgentChat.Abstractions; | |||
| // TODO: This likely should live as a "Component" in an Agent-building ClassLib? | |||
| // It seems like it could have applicability beyond AgentChat. | |||
| public static class ReflectionExtensions | |||
| { | |||
| public static AIFunctionParameterMetadata ToAIFunctionMetadata(this ParameterInfo pi) | |||
| { | |||
| return new AIFunctionParameterMetadata(pi.Name!) | |||
| { | |||
| Description = pi.GetCustomAttribute<DescriptionAttribute>()?.Description, | |||
| ParameterType = pi.ParameterType, | |||
| HasDefaultValue = pi.HasDefaultValue, | |||
| IsRequired = !pi.HasDefaultValue, | |||
| DefaultValue = pi.DefaultValue, | |||
| // Schema = JSONSchema of type | |||
| }; | |||
| } | |||
| public static AIFunctionReturnParameterMetadata ToAIFunctionReturnMetadata(this ParameterInfo rpi) | |||
| { | |||
| return new AIFunctionReturnParameterMetadata | |||
| { | |||
| Description = rpi.GetCustomAttribute<DescriptionAttribute>()?.Description, | |||
| ParameterType = rpi.ParameterType | |||
| //Schema = JSONSchema of type | |||
| }; | |||
| } | |||
| } | |||
| public class ParameterSchema(string name, Type type, bool isRequired = false, object? defaultValue = default) | |||
| { | |||
| public string Name { get; } = name; | |||
| @@ -54,15 +22,6 @@ public class ParameterSchema(string name, Type type, bool isRequired = false, ob | |||
| Type parameterType = parameterInfo.ParameterType; | |||
| return ParameterSchema<object>.Create(parameterType, parameterInfo.Name!, parameterInfo.HasDefaultValue, parameterInfo.DefaultValue); | |||
| } | |||
| public static implicit operator ParameterSchema(AIFunctionParameterMetadata parameterMetadata) | |||
| { | |||
| Type parameterType = parameterMetadata.ParameterType!; // TODO: Deal with missing ParameterTypes | |||
| return ParameterSchema<object>.Create(parameterType, | |||
| parameterMetadata.Name, | |||
| parameterMetadata.IsRequired, | |||
| parameterMetadata.DefaultValue); | |||
| } | |||
| } | |||
| // TODO: Can this be obviated by AIFunctionParameter? | |||
| @@ -82,11 +41,10 @@ public class ParameterSchema<T>(string name, bool isRequired = false, T? default | |||
| /// </summary> | |||
| public interface ITool | |||
| { | |||
| string Name { get; } | |||
| string Description { get; } | |||
| public string Name { get; } | |||
| public string Description { get; } | |||
| public IEnumerable<ParameterSchema> Parameters { get; } | |||
| public Type ReturnType { get; } | |||
| // TODO: State serialization | |||
| @@ -136,18 +94,15 @@ public class AIFunctionTool(AIFunction aiFunction) : ITool | |||
| public AIFunction AIFunction { get; } = aiFunction; | |||
| /// <inheritdoc cref="ITool.Name" /> | |||
| public string Name => this.AIFunction.Metadata.Name; | |||
| public string Name => this.AIFunction.Name; | |||
| /// <inheritdoc cref="ITool.Description" /> | |||
| public string Description => this.AIFunction.Metadata.Description; | |||
| public string Description => this.AIFunction.Description; | |||
| /// <inheritdoc cref="ITool.Parameters" /> | |||
| public IEnumerable<ParameterSchema> Parameters => from rawParameter in this.AIFunction.Metadata.Parameters | |||
| public IEnumerable<ParameterSchema> Parameters => from rawParameter in this.AIFunction.UnderlyingMethod!.GetParameters() | |||
| select (ParameterSchema)rawParameter; | |||
| /// <inheritdoc cref="ITool.ReturnType" /> | |||
| public Type ReturnType => this.AIFunction.Metadata.ReturnParameter.ParameterType!; // TODO: Deal with missing return types | |||
| /// <inheritdoc cref="ITool.ExecuteAsync" /> | |||
| public Task<object> ExecuteAsync(IEnumerable<object> parameters, CancellationToken cancellationToken = default) | |||
| => this.ExecuteAsync(parameters, cancellationToken); | |||
| @@ -164,23 +119,6 @@ public class CallableTool(string name, string description, Delegate callable) | |||
| { | |||
| internal static AIFunction CreateAIFunction(string name, string description, Delegate callable) | |||
| { | |||
| MethodInfo methodInfo = callable.Method; | |||
| IEnumerable<AIFunctionParameterMetadata> parameters = | |||
| from parameterInfo in methodInfo.GetParameters() | |||
| select parameterInfo.ToAIFunctionMetadata(); | |||
| AIFunctionReturnParameterMetadata returnParameter = methodInfo.ReturnParameter.ToAIFunctionReturnMetadata(); | |||
| AIFunctionFactoryCreateOptions createOptions = new() | |||
| { | |||
| Name = name, | |||
| Description = description, | |||
| Parameters = parameters.ToList(), | |||
| ReturnParameter = returnParameter, | |||
| // SerializerOptions = TODO: How do we maintain consistency with Python? | |||
| }; | |||
| return AIFunctionFactory.Create(callable, createOptions); | |||
| return AIFunctionFactory.Create(callable, name: name, description: description); | |||
| } | |||
| } | |||
| @@ -114,7 +114,7 @@ public abstract class GroupChatBase<TManager> : ITeam where TManager : GroupChat | |||
| if (Activator.CreateInstance(typeof(TManager), options) is TManager result) | |||
| { | |||
| return result; | |||
| }; | |||
| } | |||
| } | |||
| catch (TargetInvocationException tie) | |||
| { | |||
| @@ -11,8 +11,8 @@ internal delegate ValueTask MessagePublishServicer(GroupChatEventBase event_, st | |||
| internal interface IGroupChatHandler : IHandle<GroupChatStart>, IHandle<GroupChatAgentResponse>, IHandle<object> | |||
| { | |||
| void AttachMessagePublishServicer(MessagePublishServicer? servicer = null); | |||
| void DetachMessagePublishServicer() => this.AttachMessagePublishServicer(null); | |||
| public void AttachMessagePublishServicer(MessagePublishServicer? servicer = null); | |||
| public void DetachMessagePublishServicer() => this.AttachMessagePublishServicer(null); | |||
| } | |||
| internal sealed class GroupChatHandlerRouter<TManager> : HostableAgentAdapter, | |||
| @@ -11,8 +11,8 @@ namespace Microsoft.AutoGen.AgentChat.Abstractions; | |||
| internal interface IOutputCollectionSink | |||
| { | |||
| void CollectMessage(AgentMessage message); | |||
| void Terminate(StopMessage message); | |||
| public void CollectMessage(AgentMessage message); | |||
| public void Terminate(StopMessage message); | |||
| } | |||
| internal sealed class OutputSink : IOutputCollectionSink | |||
| @@ -26,19 +26,19 @@ public abstract class InferenceAgent<T>( | |||
| { | |||
| protected IChatClient ChatClient { get; } = client; | |||
| private ILogger<InferenceAgent<T>>? Logger => _logger as ILogger<InferenceAgent<T>>; | |||
| private Task<ChatCompletion> CompleteAsync( | |||
| private Task<ChatResponse> CompleteAsync( | |||
| IList<ChatMessage> chatMessages, | |||
| ChatOptions? options = null, | |||
| CancellationToken cancellationToken = default) | |||
| { | |||
| return ChatClient.CompleteAsync(chatMessages, options, cancellationToken); | |||
| return ChatClient.GetResponseAsync(chatMessages, options, cancellationToken); | |||
| } | |||
| private IAsyncEnumerable<StreamingChatCompletionUpdate> CompleteStreamingAsync( | |||
| private IAsyncEnumerable<ChatResponseUpdate> CompleteStreamingAsync( | |||
| IList<ChatMessage> chatMessages, | |||
| ChatOptions? options = null, | |||
| CancellationToken cancellationToken = default) | |||
| { | |||
| return ChatClient.CompleteStreamingAsync(chatMessages, options, cancellationToken); | |||
| return ChatClient.GetStreamingResponseAsync(chatMessages, options, cancellationToken); | |||
| } | |||
| } | |||
| @@ -43,7 +43,7 @@ internal sealed class AutoRestartChannel : IDisposable | |||
| if (this.RecreateChannel(null) == null) | |||
| { | |||
| throw new Exception("Failed to connect to gRPC endpoint."); | |||
| }; | |||
| } | |||
| } | |||
| public AsyncDuplexStreamingCall<Message, Message> StreamingCall | |||
| @@ -117,8 +117,9 @@ internal sealed class GrpcMessageRouter(AgentRpc.AgentRpcClient client, | |||
| private readonly CancellationTokenSource _shutdownCts = CancellationTokenSource.CreateLinkedTokenSource(shutdownCancellation); | |||
| private readonly IMessageSink<Message> _incomingMessageSink = incomingMessageSink; | |||
| // TODO: Enable a way to configure the channel options | |||
| private readonly Channel<(Message Message, TaskCompletionSource WriteCompletionSource)> _outboundMessagesChannel | |||
| // TODO: Enable a way to configure the channel options | |||
| = Channel.CreateBounded<(Message, TaskCompletionSource)>(DefaultChannelOptions); | |||
| private readonly AutoRestartChannel _incomingMessageChannel = new AutoRestartChannel(client, clientId, logger, shutdownCancellation); | |||
| @@ -7,11 +7,11 @@ namespace Microsoft.AutoGen.Core; | |||
| internal interface IResultSink<TResult> : IValueTaskSource<TResult> | |||
| { | |||
| void SetResult(TResult result); | |||
| void SetException(Exception exception); | |||
| void SetCancelled(); | |||
| public void SetResult(TResult result); | |||
| public void SetException(Exception exception); | |||
| public void SetCancelled(); | |||
| ValueTask<TResult> Future { get; } | |||
| public ValueTask<TResult> Future { get; } | |||
| } | |||
| public sealed class ResultSink<TResult> : IResultSink<TResult> | |||
| @@ -41,12 +41,13 @@ public static class ServiceCollectionChatClientExtensions | |||
| Func<ChatClientBuilder, ChatClientBuilder>? builder = null) | |||
| { | |||
| uri ??= new Uri("http://localhost:11434"); | |||
| return services.AddChatClient(pipeline => | |||
| services.AddChatClient(service => | |||
| { | |||
| builder?.Invoke(pipeline); | |||
| var httpClient = pipeline.Services.GetService<HttpClient>() ?? new(); | |||
| return pipeline.Use(new OllamaChatClient(uri, modelName, httpClient)); | |||
| var httpClient = service.GetService<HttpClient>() ?? new(); | |||
| return new OllamaChatClient(uri, modelName, httpClient); | |||
| }); | |||
| return services; | |||
| } | |||
| public static IServiceCollection AddOpenAIChatClient( | |||
| this IHostApplicationBuilder hostBuilder, | |||
| @@ -81,16 +82,17 @@ public static class ServiceCollectionChatClientExtensions | |||
| Uri? endpoint = null, | |||
| Func<ChatClientBuilder, ChatClientBuilder>? builder = null) | |||
| { | |||
| return services | |||
| services | |||
| .AddSingleton(_ => endpoint is null | |||
| ? new OpenAIClient(apiKey) | |||
| : new AzureOpenAIClient(endpoint, new ApiKeyCredential(apiKey))) | |||
| .AddChatClient(pipeline => | |||
| .AddChatClient(service => | |||
| { | |||
| builder?.Invoke(pipeline); | |||
| var openAiClient = pipeline.Services.GetRequiredService<OpenAIClient>(); | |||
| return pipeline.Use(openAiClient.AsChatClient(modelOrDeploymentName)); | |||
| var openAiClient = service.GetRequiredService<OpenAIClient>(); | |||
| return openAiClient.AsChatClient(modelOrDeploymentName); | |||
| }); | |||
| return services; | |||
| } | |||
| public static IServiceCollection AddAzureChatClient( | |||
| this IHostApplicationBuilder hostBuilder, | |||
| @@ -109,12 +111,10 @@ public static class ServiceCollectionChatClientExtensions | |||
| } | |||
| var endpoint = $"{serviceName}:Endpoint" ?? throw new InvalidOperationException($"No endpoint was specified for the Azure Inference Chat Client"); | |||
| var endpointUri = string.IsNullOrEmpty(endpoint) ? null : new Uri(endpoint); | |||
| return hostBuilder.Services.AddChatClient(pipeline => | |||
| { | |||
| builder?.Invoke(pipeline); | |||
| var token = Environment.GetEnvironmentVariable("GH_TOKEN") ?? throw new InvalidOperationException("No model access token was found in the environment variable GH_TOKEN"); | |||
| return pipeline.Use(new ChatCompletionsClient( | |||
| endpointUri, new AzureKeyCredential(token)).AsChatClient(modelOrDeploymentName)); | |||
| }); | |||
| var token = Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY") ?? throw new InvalidOperationException("No model access token was found in the environment variable AZURE_OPENAI_API_KEY"); | |||
| var chatClient = new ChatCompletionsClient(endpointUri, new AzureKeyCredential(token)).AsChatClient(modelOrDeploymentName); | |||
| hostBuilder.Services.AddChatClient(chatClient); | |||
| return hostBuilder.Services; | |||
| } | |||
| } | |||
| @@ -243,7 +243,7 @@ public sealed class GrpcGateway : BackgroundService, IGateway | |||
| default: | |||
| await RespondBadRequestAsync(connection, $"Unknown message type for message '{message}'."); | |||
| break; | |||
| }; | |||
| } | |||
| } | |||
| /// <summary> | |||
| @@ -2,7 +2,7 @@ | |||
| <PropertyGroup> | |||
| <OutputType>Exe</OutputType> | |||
| <TargetFramework>net8.0</TargetFramework> | |||
| <TargetFramework>net8.0</TargetFramework> | |||
| <ImplicitUsings>enable</ImplicitUsings> | |||
| <Nullable>enable</Nullable> | |||
| <PublishAot>true</PublishAot> | |||
| @@ -12,6 +12,12 @@ | |||
| "ImageUri": null, | |||
| "ImageBytes": null, | |||
| "ImageBytesMediaType": null, | |||
| "InputAudioBytes": null, | |||
| "InputAudioFormat": null, | |||
| "FileId": null, | |||
| "FileBytes": null, | |||
| "FileBytesMediaType": null, | |||
| "Filename": null, | |||
| "ImageDetailLevel": null, | |||
| "Refusal": null | |||
| } | |||
| @@ -31,6 +37,12 @@ | |||
| "ImageUri": null, | |||
| "ImageBytes": null, | |||
| "ImageBytesMediaType": null, | |||
| "InputAudioBytes": null, | |||
| "InputAudioFormat": null, | |||
| "FileId": null, | |||
| "FileBytes": null, | |||
| "FileBytesMediaType": null, | |||
| "Filename": null, | |||
| "ImageDetailLevel": null, | |||
| "Refusal": null | |||
| } | |||
| @@ -57,6 +69,12 @@ | |||
| "ImageUri": null, | |||
| "ImageBytes": null, | |||
| "ImageBytesMediaType": null, | |||
| "InputAudioBytes": null, | |||
| "InputAudioFormat": null, | |||
| "FileId": null, | |||
| "FileBytes": null, | |||
| "FileBytesMediaType": null, | |||
| "Filename": null, | |||
| "ImageDetailLevel": null, | |||
| "Refusal": null | |||
| } | |||
| @@ -78,6 +96,12 @@ | |||
| "ImageUri": "https://example.com/image.png", | |||
| "ImageBytes": null, | |||
| "ImageBytesMediaType": null, | |||
| "InputAudioBytes": null, | |||
| "InputAudioFormat": null, | |||
| "FileId": null, | |||
| "FileBytes": null, | |||
| "FileBytesMediaType": null, | |||
| "Filename": null, | |||
| "ImageDetailLevel": null, | |||
| "Refusal": null | |||
| } | |||
| @@ -104,6 +128,12 @@ | |||
| "ImageUri": null, | |||
| "ImageBytes": null, | |||
| "ImageBytesMediaType": null, | |||
| "InputAudioBytes": null, | |||
| "InputAudioFormat": null, | |||
| "FileId": null, | |||
| "FileBytes": null, | |||
| "FileBytesMediaType": null, | |||
| "Filename": null, | |||
| "ImageDetailLevel": null, | |||
| "Refusal": null | |||
| }, | |||
| @@ -113,6 +143,12 @@ | |||
| "ImageUri": "https://example.com/image.png", | |||
| "ImageBytes": null, | |||
| "ImageBytesMediaType": null, | |||
| "InputAudioBytes": null, | |||
| "InputAudioFormat": null, | |||
| "FileId": null, | |||
| "FileBytes": null, | |||
| "FileBytesMediaType": null, | |||
| "Filename": null, | |||
| "ImageDetailLevel": null, | |||
| "Refusal": null | |||
| } | |||
| @@ -0,0 +1,260 @@ | |||
| [ | |||
| { | |||
| "OriginalMessage": "TextMessage(system, You are a helpful AI assistant, )", | |||
| "ConvertedMessages": [ | |||
| { | |||
| "Name": null, | |||
| "Role": "system", | |||
| "Content": [ | |||
| { | |||
| "Kind": 0, | |||
| "Text": "You are a helpful AI assistant", | |||
| "ImageUri": null, | |||
| "ImageBytes": null, | |||
| "ImageBytesMediaType": null, | |||
| "InputAudioBytes": null, | |||
| "InputAudioFormat": null, | |||
| "FileId": null, | |||
| "FileBytes": null, | |||
| "FileBytesMediaType": null, | |||
| "Filename": null, | |||
| "ImageDetailLevel": null, | |||
| "Refusal": null | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "OriginalMessage": "TextMessage(user, Hello, user)", | |||
| "ConvertedMessages": [ | |||
| { | |||
| "Role": "user", | |||
| "Content": [ | |||
| { | |||
| "Kind": 0, | |||
| "Text": "Hello", | |||
| "ImageUri": null, | |||
| "ImageBytes": null, | |||
| "ImageBytesMediaType": null, | |||
| "InputAudioBytes": null, | |||
| "InputAudioFormat": null, | |||
| "FileId": null, | |||
| "FileBytes": null, | |||
| "FileBytesMediaType": null, | |||
| "Filename": null, | |||
| "ImageDetailLevel": null, | |||
| "Refusal": null | |||
| } | |||
| ], | |||
| "Name": "user", | |||
| "MultiModaItem": [ | |||
| { | |||
| "Type": "Text", | |||
| "Text": "Hello" | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "OriginalMessage": "TextMessage(assistant, How can I help you?, assistant)", | |||
| "ConvertedMessages": [ | |||
| { | |||
| "Role": "assistant", | |||
| "Content": [ | |||
| { | |||
| "Kind": 0, | |||
| "Text": "How can I help you?", | |||
| "ImageUri": null, | |||
| "ImageBytes": null, | |||
| "ImageBytesMediaType": null, | |||
| "InputAudioBytes": null, | |||
| "InputAudioFormat": null, | |||
| "FileId": null, | |||
| "FileBytes": null, | |||
| "FileBytesMediaType": null, | |||
| "Filename": null, | |||
| "ImageDetailLevel": null, | |||
| "Refusal": null | |||
| } | |||
| ], | |||
| "Name": "assistant", | |||
| "TooCall": [] | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "OriginalMessage": "ImageMessage(user, https://example.com/image.png, user)", | |||
| "ConvertedMessages": [ | |||
| { | |||
| "Role": "user", | |||
| "Content": [ | |||
| { | |||
| "Kind": 2, | |||
| "Text": null, | |||
| "ImageUri": "https://example.com/image.png", | |||
| "ImageBytes": null, | |||
| "ImageBytesMediaType": null, | |||
| "InputAudioBytes": null, | |||
| "InputAudioFormat": null, | |||
| "FileId": null, | |||
| "FileBytes": null, | |||
| "FileBytesMediaType": null, | |||
| "Filename": null, | |||
| "ImageDetailLevel": null, | |||
| "Refusal": null | |||
| } | |||
| ], | |||
| "Name": "user", | |||
| "MultiModaItem": [ | |||
| { | |||
| "Type": "Image", | |||
| "ImageUrl": "https://example.com/image.png" | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "OriginalMessage": "MultiModalMessage(assistant, user)\n\tTextMessage(user, Hello, user)\n\tImageMessage(user, https://example.com/image.png, user)", | |||
| "ConvertedMessages": [ | |||
| { | |||
| "Role": "user", | |||
| "Content": [ | |||
| { | |||
| "Kind": 0, | |||
| "Text": "Hello", | |||
| "ImageUri": null, | |||
| "ImageBytes": null, | |||
| "ImageBytesMediaType": null, | |||
| "InputAudioBytes": null, | |||
| "InputAudioFormat": null, | |||
| "FileId": null, | |||
| "FileBytes": null, | |||
| "FileBytesMediaType": null, | |||
| "Filename": null, | |||
| "ImageDetailLevel": null, | |||
| "Refusal": null | |||
| }, | |||
| { | |||
| "Kind": 2, | |||
| "Text": null, | |||
| "ImageUri": "https://example.com/image.png", | |||
| "ImageBytes": null, | |||
| "ImageBytesMediaType": null, | |||
| "InputAudioBytes": null, | |||
| "InputAudioFormat": null, | |||
| "FileId": null, | |||
| "FileBytes": null, | |||
| "FileBytesMediaType": null, | |||
| "Filename": null, | |||
| "ImageDetailLevel": null, | |||
| "Refusal": null | |||
| } | |||
| ], | |||
| "Name": "user", | |||
| "MultiModaItem": [ | |||
| { | |||
| "Type": "Text", | |||
| "Text": "Hello" | |||
| }, | |||
| { | |||
| "Type": "Image", | |||
| "ImageUrl": "https://example.com/image.png" | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "OriginalMessage": "ToolCallMessage(assistant)\n\tToolCall(test, test, )", | |||
| "ConvertedMessages": [ | |||
| { | |||
| "Role": "assistant", | |||
| "Content": [], | |||
| "Name": null, | |||
| "TooCall": [ | |||
| { | |||
| "Type": "Function", | |||
| "Name": "test", | |||
| "Arguments": "dGVzdA==", | |||
| "Id": "test" | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "OriginalMessage": "ToolCallResultMessage(user)\n\tToolCall(test, test, result)", | |||
| "ConvertedMessages": [ | |||
| { | |||
| "Role": "tool", | |||
| "Content": "result", | |||
| "ToolCallId": "test" | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "OriginalMessage": "ToolCallResultMessage(user)\n\tToolCall(result, test, test)\n\tToolCall(result, test, test)", | |||
| "ConvertedMessages": [ | |||
| { | |||
| "Role": "tool", | |||
| "Content": "test", | |||
| "ToolCallId": "result_0" | |||
| }, | |||
| { | |||
| "Role": "tool", | |||
| "Content": "test", | |||
| "ToolCallId": "result_1" | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "OriginalMessage": "ToolCallMessage(assistant)\n\tToolCall(test, test, )\n\tToolCall(test, test, )", | |||
| "ConvertedMessages": [ | |||
| { | |||
| "Role": "assistant", | |||
| "Content": [], | |||
| "Name": null, | |||
| "TooCall": [ | |||
| { | |||
| "Type": "Function", | |||
| "Name": "test", | |||
| "Arguments": "dGVzdA==", | |||
| "Id": "test_0" | |||
| }, | |||
| { | |||
| "Type": "Function", | |||
| "Name": "test", | |||
| "Arguments": "dGVzdA==", | |||
| "Id": "test_1" | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "OriginalMessage": "AggregateMessage(assistant)\n\tToolCallMessage(assistant)\n\tToolCall(test, test, )\n\tToolCallResultMessage(assistant)\n\tToolCall(test, test, result)", | |||
| "ConvertedMessages": [ | |||
| { | |||
| "Role": "assistant", | |||
| "Content": [], | |||
| "Name": null, | |||
| "TooCall": [ | |||
| { | |||
| "Type": "Function", | |||
| "Name": "test", | |||
| "Arguments": "dGVzdA==", | |||
| "Id": "test" | |||
| } | |||
| ] | |||
| }, | |||
| { | |||
| "Role": "tool", | |||
| "Content": "result", | |||
| "ToolCallId": "test" | |||
| } | |||
| ] | |||
| } | |||
| ] | |||
| @@ -60,7 +60,7 @@ public class FunctionTests | |||
| GetWeatherAsyncStatic, | |||
| ]; | |||
| var functionContracts = availableDelegates.Select(function => (FunctionContract)AIFunctionFactory.Create(function).Metadata).ToList(); | |||
| var functionContracts = availableDelegates.Select(function => (FunctionContract)AIFunctionFactory.Create(function)).ToList(); | |||
| // Verify the function contracts | |||
| functionContracts.Should().HaveCount(4); | |||
| @@ -72,7 +72,7 @@ public partial class MiddlewareTest | |||
| public async Task FunctionCallMiddlewareTestAsync() | |||
| { | |||
| var agent = new EchoAgent("echo"); | |||
| var args = new EchoSchema { message = "hello" }; | |||
| var args = new EchoSchema { message = "hello" }; // make the format check happy on linux | |||
| var argsJson = JsonSerializer.Serialize(args) ?? throw new InvalidOperationException("Failed to serialize args"); | |||
| var functionCall = new ToolCall("Echo", argsJson); | |||
| var functionCallAgent = agent.RegisterMiddleware(async (messages, options, agent, ct) => | |||
| @@ -92,7 +92,7 @@ internal sealed class TestGrpcWorkerConnection : IAsyncDisposable | |||
| default: | |||
| // if it wasn't recognized return bad request | |||
| throw new RpcException(new Status(StatusCode.InvalidArgument, $"Unknown message type for message '{message}'")); | |||
| }; | |||
| } | |||
| } | |||
| } | |||
| catch (OperationCanceledException) | |||