|
| 1 | +// Licensed to the .NET Foundation under one or more agreements. |
| 2 | +// The .NET Foundation licenses this file to you under the MIT license. |
| 3 | + |
| 4 | +usingSystem; |
| 5 | +usingSystem.Collections.Generic; |
| 6 | +usingSystem.ComponentModel; |
| 7 | +usingSystem.Linq; |
| 8 | +usingSystem.Text.Json; |
| 9 | +usingOpenAI.Assistants; |
| 10 | +usingOpenAI.Chat; |
| 11 | +usingOpenAI.Realtime; |
| 12 | +usingOpenAI.Responses; |
| 13 | +usingXunit; |
| 14 | + |
| 15 | +namespaceMicrosoft.Extensions.AI; |
| 16 | + |
| 17 | +publicclassOpenAIConversionTests |
| 18 | +{ |
| 19 | +privatestaticreadonlyAIFunction_testFunction=AIFunctionFactory.Create( |
| 20 | +([Description("The name parameter")]stringname)=>name, |
| 21 | +"test_function", |
| 22 | +"A test function for conversion"); |
| 23 | + |
| 24 | +[Fact] |
| 25 | +publicvoidAsOpenAIChatTool_ProducesValidInstance() |
| 26 | +{ |
| 27 | +vartool=_testFunction.AsOpenAIChatTool(); |
| 28 | + |
| 29 | +Assert.NotNull(tool); |
| 30 | +Assert.Equal("test_function",tool.FunctionName); |
| 31 | +Assert.Equal("A test function for conversion",tool.FunctionDescription); |
| 32 | +ValidateSchemaParameters(tool.FunctionParameters); |
| 33 | +} |
| 34 | + |
| 35 | +[Fact] |
| 36 | +publicvoidAsOpenAIResponseTool_ProducesValidInstance() |
| 37 | +{ |
| 38 | +vartool=_testFunction.AsOpenAIResponseTool(); |
| 39 | + |
| 40 | +Assert.NotNull(tool); |
| 41 | +} |
| 42 | + |
| 43 | +[Fact] |
| 44 | +publicvoidAsOpenAIConversationFunctionTool_ProducesValidInstance() |
| 45 | +{ |
| 46 | +vartool=_testFunction.AsOpenAIConversationFunctionTool(); |
| 47 | + |
| 48 | +Assert.NotNull(tool); |
| 49 | +Assert.Equal("test_function",tool.Name); |
| 50 | +Assert.Equal("A test function for conversion",tool.Description); |
| 51 | +ValidateSchemaParameters(tool.Parameters); |
| 52 | +} |
| 53 | + |
| 54 | +[Fact] |
| 55 | +publicvoidAsOpenAIAssistantsFunctionToolDefinition_ProducesValidInstance() |
| 56 | +{ |
| 57 | +vartool=_testFunction.AsOpenAIAssistantsFunctionToolDefinition(); |
| 58 | + |
| 59 | +Assert.NotNull(tool); |
| 60 | +Assert.Equal("test_function",tool.FunctionName); |
| 61 | +Assert.Equal("A test function for conversion",tool.Description); |
| 62 | +ValidateSchemaParameters(tool.Parameters); |
| 63 | +} |
| 64 | + |
| 65 | +/// <summary>Helper method to validate function parameters match our schema.</summary> |
| 66 | +privatestaticvoidValidateSchemaParameters(BinaryDataparameters) |
| 67 | +{ |
| 68 | +Assert.NotNull(parameters); |
| 69 | + |
| 70 | +usingvarjsonDoc=JsonDocument.Parse(parameters); |
| 71 | +varroot=jsonDoc.RootElement; |
| 72 | + |
| 73 | +Assert.Equal("object",root.GetProperty("type").GetString()); |
| 74 | +Assert.True(root.TryGetProperty("properties",outvarproperties)); |
| 75 | +Assert.True(properties.TryGetProperty("name",outvarnameProperty)); |
| 76 | +Assert.Equal("string",nameProperty.GetProperty("type").GetString()); |
| 77 | +Assert.Equal("The name parameter",nameProperty.GetProperty("description").GetString()); |
| 78 | +} |
| 79 | + |
| 80 | +[Fact] |
| 81 | +publicvoidAsOpenAIChatMessages_ProducesExpectedOutput() |
| 82 | +{ |
| 83 | +Assert.Throws<ArgumentNullException>("messages",()=>((IEnumerable<ChatMessage>)null!).AsOpenAIChatMessages()); |
| 84 | + |
| 85 | +List<ChatMessage>messages= |
| 86 | +[ |
| 87 | +new(ChatRole.System,"You are a helpful assistant."), |
| 88 | +new(ChatRole.User,"Hello"), |
| 89 | +new(ChatRole.Assistant, |
| 90 | +[ |
| 91 | +newTextContent("Hi there!"), |
| 92 | +newFunctionCallContent("callid123","SomeFunction",newDictionary<string,object?> |
| 93 | +{ |
| 94 | +["param1"]="value1", |
| 95 | +["param2"]=42 |
| 96 | +}), |
| 97 | +]), |
| 98 | +new(ChatRole.Tool,[newFunctionResultContent("callid123","theresult")]), |
| 99 | +new(ChatRole.Assistant,"The answer is 42."), |
| 100 | +]; |
| 101 | + |
| 102 | +varconvertedMessages=messages.AsOpenAIChatMessages().ToArray(); |
| 103 | + |
| 104 | +Assert.Equal(5,convertedMessages.Length); |
| 105 | + |
| 106 | +SystemChatMessagem0=Assert.IsType<SystemChatMessage>(convertedMessages[0]); |
| 107 | +Assert.Equal("You are a helpful assistant.",Assert.Single(m0.Content).Text); |
| 108 | + |
| 109 | +UserChatMessagem1=Assert.IsType<UserChatMessage>(convertedMessages[1]); |
| 110 | +Assert.Equal("Hello",Assert.Single(m1.Content).Text); |
| 111 | + |
| 112 | +AssistantChatMessagem2=Assert.IsType<AssistantChatMessage>(convertedMessages[2]); |
| 113 | +Assert.Single(m2.Content); |
| 114 | +Assert.Equal("Hi there!",m2.Content[0].Text); |
| 115 | +vartc=Assert.Single(m2.ToolCalls); |
| 116 | +Assert.Equal("callid123",tc.Id); |
| 117 | +Assert.Equal("SomeFunction",tc.FunctionName); |
| 118 | +Assert.True(JsonElement.DeepEquals(JsonSerializer.SerializeToElement(newDictionary<string,object?> |
| 119 | +{ |
| 120 | +["param1"]="value1", |
| 121 | +["param2"]=42 |
| 122 | +}),JsonSerializer.Deserialize<JsonElement>(tc.FunctionArguments.ToMemory().Span))); |
| 123 | + |
| 124 | +ToolChatMessagem3=Assert.IsType<ToolChatMessage>(convertedMessages[3]); |
| 125 | +Assert.Equal("callid123",m3.ToolCallId); |
| 126 | +Assert.Equal("theresult",Assert.Single(m3.Content).Text); |
| 127 | + |
| 128 | +AssistantChatMessagem4=Assert.IsType<AssistantChatMessage>(convertedMessages[4]); |
| 129 | +Assert.Equal("The answer is 42.",Assert.Single(m4.Content).Text); |
| 130 | +} |
| 131 | + |
| 132 | +[Fact] |
| 133 | +publicvoidAsOpenAIResponseItems_ProducesExpectedOutput() |
| 134 | +{ |
| 135 | +Assert.Throws<ArgumentNullException>("messages",()=>((IEnumerable<ChatMessage>)null!).AsOpenAIResponseItems()); |
| 136 | + |
| 137 | +List<ChatMessage>messages= |
| 138 | +[ |
| 139 | +new(ChatRole.System,"You are a helpful assistant."), |
| 140 | +new(ChatRole.User,"Hello"), |
| 141 | +new(ChatRole.Assistant, |
| 142 | +[ |
| 143 | +newTextContent("Hi there!"), |
| 144 | +newFunctionCallContent("callid123","SomeFunction",newDictionary<string,object?> |
| 145 | +{ |
| 146 | +["param1"]="value1", |
| 147 | +["param2"]=42 |
| 148 | +}), |
| 149 | +]), |
| 150 | +new(ChatRole.Tool,[newFunctionResultContent("callid123","theresult")]), |
| 151 | +new(ChatRole.Assistant,"The answer is 42."), |
| 152 | +]; |
| 153 | + |
| 154 | +varconvertedItems=messages.AsOpenAIResponseItems().ToArray(); |
| 155 | + |
| 156 | +Assert.Equal(6,convertedItems.Length); |
| 157 | + |
| 158 | +MessageResponseItemm0=Assert.IsAssignableFrom<MessageResponseItem>(convertedItems[0]); |
| 159 | +Assert.Equal("You are a helpful assistant.",Assert.Single(m0.Content).Text); |
| 160 | + |
| 161 | +MessageResponseItemm1=Assert.IsAssignableFrom<MessageResponseItem>(convertedItems[1]); |
| 162 | +Assert.Equal(OpenAI.Responses.MessageRole.User,m1.Role); |
| 163 | +Assert.Equal("Hello",Assert.Single(m1.Content).Text); |
| 164 | + |
| 165 | +MessageResponseItemm2=Assert.IsAssignableFrom<MessageResponseItem>(convertedItems[2]); |
| 166 | +Assert.Equal(OpenAI.Responses.MessageRole.Assistant,m2.Role); |
| 167 | +Assert.Equal("Hi there!",Assert.Single(m2.Content).Text); |
| 168 | + |
| 169 | +FunctionCallResponseItemm3=Assert.IsAssignableFrom<FunctionCallResponseItem>(convertedItems[3]); |
| 170 | +Assert.Equal("callid123",m3.CallId); |
| 171 | +Assert.Equal("SomeFunction",m3.FunctionName); |
| 172 | +Assert.True(JsonElement.DeepEquals(JsonSerializer.SerializeToElement(newDictionary<string,object?> |
| 173 | +{ |
| 174 | +["param1"]="value1", |
| 175 | +["param2"]=42 |
| 176 | +}),JsonSerializer.Deserialize<JsonElement>(m3.FunctionArguments.ToMemory().Span))); |
| 177 | + |
| 178 | +FunctionCallOutputResponseItemm4=Assert.IsAssignableFrom<FunctionCallOutputResponseItem>(convertedItems[4]); |
| 179 | +Assert.Equal("callid123",m4.CallId); |
| 180 | +Assert.Equal("theresult",m4.FunctionOutput); |
| 181 | + |
| 182 | +MessageResponseItemm5=Assert.IsAssignableFrom<MessageResponseItem>(convertedItems[5]); |
| 183 | +Assert.Equal(OpenAI.Responses.MessageRole.Assistant,m5.Role); |
| 184 | +Assert.Equal("The answer is 42.",Assert.Single(m5.Content).Text); |
| 185 | +} |
| 186 | +} |