diff --git a/src/GenerativeAI.Microsoft/Extensions/MicrosoftExtensions.cs b/src/GenerativeAI.Microsoft/Extensions/MicrosoftExtensions.cs index 006a351b..42a76afb 100644 --- a/src/GenerativeAI.Microsoft/Extensions/MicrosoftExtensions.cs +++ b/src/GenerativeAI.Microsoft/Extensions/MicrosoftExtensions.cs @@ -146,7 +146,7 @@ where p is not null /// A object representing the given AI content, or null if the content type is unsupported. public static Part? ToPart(this AIContent content, ChatOptions? options = null) { - return content switch + var part = content switch { TextContent text => new Part { Text = text.Text }, DataContent image => new Part @@ -175,6 +175,15 @@ where p is not null }, _ => null, }; + + if (part != null && content.AdditionalProperties != null && + content.AdditionalProperties.TryGetValue(AdditionalPropertiesKeys.ThoughtSignature, out var signature) && + signature is string signatureString) + { + part.ThoughtSignature = signatureString; + } + + return part; } /// @@ -590,13 +599,25 @@ public static IList ToAiContents(this List? parts, ChatOptions? { if (part.Text is not null) { - (contents ??= new()).Add(new TextContent(part.Text)); + var textContent = new TextContent(part.Text); + if (part.ThoughtSignature != null) + { + textContent.AdditionalProperties ??= new AdditionalPropertiesDictionary(); + textContent.AdditionalProperties[AdditionalPropertiesKeys.ThoughtSignature] = part.ThoughtSignature; + } + (contents ??= new()).Add(textContent); } if (part.FunctionCall is not null) { - (contents ??= new()).Add(new FunctionCallContent(part.FunctionCall.Name, part.FunctionCall.Name, - ConvertFunctionCallArg(part.FunctionCall.Args, part.FunctionCall.Name, options))); + var functionCallContent = new FunctionCallContent(part.FunctionCall.Name, part.FunctionCall.Name, + ConvertFunctionCallArg(part.FunctionCall.Args, part.FunctionCall.Name, options)); + if (part.ThoughtSignature != null) + { + functionCallContent.AdditionalProperties ??= new AdditionalPropertiesDictionary(); + functionCallContent.AdditionalProperties[AdditionalPropertiesKeys.ThoughtSignature] = part.ThoughtSignature; + } + (contents ??= new()).Add(functionCallContent); } if (part.FunctionResponse is not null) diff --git a/tests/GenerativeAI.Microsoft.Tests/MicrosoftExtension_Tests.cs b/tests/GenerativeAI.Microsoft.Tests/MicrosoftExtension_Tests.cs index b18a3eef..3c137c18 100644 --- a/tests/GenerativeAI.Microsoft.Tests/MicrosoftExtension_Tests.cs +++ b/tests/GenerativeAI.Microsoft.Tests/MicrosoftExtension_Tests.cs @@ -310,6 +310,102 @@ public void ToAiContents_BogusData_HandlesVariousParts() // Add more specific assertions based on your Bogus rules if needed } + [Fact] + public void ToAiContents_WithFunctionCallPart_AndThoughtSignature_ReturnsFunctionCallContentWithSignature() + { + // Arrange + var thoughtSignature = "some_signature"; + var parts = new List { new Part + { + FunctionCall = new FunctionCall { Name = "myFunction", Args = JsonNode.Parse(" { \"arg1\": \"value1\", \"arg2\": \"value2\" }") }, + ThoughtSignature = thoughtSignature + } }; + + // Act + var result = parts.ToAiContents(); + + // Assert + result.ShouldNotBeNull(); + result.Count.ShouldBe(1); + result.FirstOrDefault().ShouldBeOfType(); + var functionCallContent = (FunctionCallContent)result.FirstOrDefault(); + functionCallContent.Name.ShouldBe("myFunction"); + functionCallContent.AdditionalProperties.ShouldNotBeNull(); + functionCallContent.AdditionalProperties.ContainsKey(AdditionalPropertiesKeys.ThoughtSignature).ShouldBeTrue(); + functionCallContent.AdditionalProperties[AdditionalPropertiesKeys.ThoughtSignature].ShouldBe(thoughtSignature); + } + + [Fact] + public void ToPart_WithFunctionCallContent_AndThoughtSignature_ReturnsPartWithSignature() + { + // Arrange + var thoughtSignature = "some_signature"; + var content = new FunctionCallContent("myFunction", "name") + { + Arguments = new Dictionary() + { + {"arg1", "value1"} + } + }; + content.AdditionalProperties = new AdditionalPropertiesDictionary + { + { AdditionalPropertiesKeys.ThoughtSignature, thoughtSignature } + }; + + // Act + var part = content.ToPart(); + + // Assert + part.ShouldNotBeNull(); + part.FunctionCall.ShouldNotBeNull(); + part.ThoughtSignature.ShouldBe(thoughtSignature); + } + + [Fact] + public void ToAiContents_WithTextPart_AndThoughtSignature_ReturnsTextContentWithSignature() + { + // Arrange + var thoughtSignature = "some_signature"; + var parts = new List { new Part + { + Text = "Hello world", + ThoughtSignature = thoughtSignature + } }; + + // Act + var result = parts.ToAiContents(); + + // Assert + result.ShouldNotBeNull(); + result.Count.ShouldBe(1); + result.FirstOrDefault().ShouldBeOfType(); + var textContent = (TextContent)result.FirstOrDefault(); + textContent.Text.ShouldBe("Hello world"); + textContent.AdditionalProperties.ShouldNotBeNull(); + textContent.AdditionalProperties.ContainsKey(AdditionalPropertiesKeys.ThoughtSignature).ShouldBeTrue(); + textContent.AdditionalProperties[AdditionalPropertiesKeys.ThoughtSignature].ShouldBe(thoughtSignature); + } + + [Fact] + public void ToPart_WithTextContent_AndThoughtSignature_ReturnsPartWithSignature() + { + // Arrange + var thoughtSignature = "some_signature"; + var content = new TextContent("Hello world"); + content.AdditionalProperties = new AdditionalPropertiesDictionary + { + { AdditionalPropertiesKeys.ThoughtSignature, thoughtSignature } + }; + + // Act + var part = content.ToPart(); + + // Assert + part.ShouldNotBeNull(); + part.Text.ShouldBe("Hello world"); + part.ThoughtSignature.ShouldBe(thoughtSignature); + } + #endregion #region ToGenerateContentRequest