Skip to content

.Net: Bug: complex function parameter passed as stringified JSON rather than deserialized objects #10713

Closed
@adv12

Description

@adv12

Describe the bug
Semantic Kernel attempts to call a native function with a typed array for a parameter with the stringified JSON of its content rather than the deserialized array. This only seems to happen if the array type has an enum property.

To Reproduce

Create a Semantic Kernel project with the following Program.cs:

using System.ComponentModel;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

string model = "gpt-4o";

Console.Write("Enter your OpenAI API key: ");
string apiKey = Console.ReadLine();
Console.Write("Enter your OpenAI OrgId: ");
string orgId = Console.ReadLine();

IKernelBuilder builder = Kernel.CreateBuilder();
builder.AddOpenAIChatCompletion(model, apiKey, orgId, null, new HttpClient(new LoggingHttpClientHandler()));
Kernel kernel = builder.Build();

kernel.ImportPluginFromType<MyPlugin>();

OpenAIPromptExecutionSettings settings =
   new() { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() };

IChatCompletionService chatCompletionService =
   kernel.GetRequiredService<IChatCompletionService>();

ChatHistory chatHistory = [];

chatHistory.AddUserMessage("Set Item1 to 5.");

foreach (var content in await chatCompletionService.GetChatMessageContentsAsync(chatHistory, settings, kernel))
{
   Console.WriteLine(content.Content);
}

enum ItemName
{
   Item1,
   Item2,
   Item3
}

class Item
{
   [Description("The name.")]
   public ItemName Name { get; set; }
   [Description("The value.")]
   public int Value { get; set; }
}

class MyPlugin
{
   [KernelFunction]
   [Description("Sets the values of the specified items.")]
   public Task SetItemValues(Kernel kernel, Item[] items)
   {
      foreach (Item item in items)
      {
         Console.WriteLine($"Setting {item.Name} to {item.Value}!");
      }
      return Task.CompletedTask;
   }
}

class LoggingHttpClientHandler : HttpClientHandler
{
   protected override async Task<HttpResponseMessage> SendAsync(
      HttpRequestMessage request, CancellationToken cancellationToken)
   {
      string content;
      if (request.Content is not null)
      {
         content = await request.Content.ReadAsStringAsync(cancellationToken);
         Console.WriteLine(content);
      }

      HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
      MemoryStream responseStream = new(await response.Content.ReadAsByteArrayAsync(cancellationToken));
      Console.WriteLine(new StreamReader(responseStream).ReadToEnd());
      responseStream.Position = 0;
      response.Content = new StreamContent(responseStream);
      return response;
   }
}

When you run this program, observe that the function parameters from OpenAI are properly formed, but Semantic Kernel tries to call SetItemValues with a stringified version of the JSON rather than the deserialized Item list.

Expected behavior
MyPlugin.SetItemValues gets called with an array of 1 Item

Platform

  • Language: C#
  • Source: NuGet package version 1.39.0
  • AI model: OpenAI:GPT-4o
  • IDE: Visual Studio
  • OS: Windows, Mac

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETIssue or Pull requests regarding .NET codebugSomething isn't workingtriage

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions