Upgraded to .NET 10 (#1147)

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-11-01 20:32:16 +01:00
1 parent faae2951f7
commit fc7399e81b
327 files changed
+3059 -3049

No files matched your search

@@ -79,12 +79,12 @@ public sealed class AnthropicMessageStreamAccumulatorTests
"""{"type":"message_stop"}""");
var block = response!.Content.Single();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(ReadString(block, "type"), Is.EqualTo("thinking"), "The block goes back as the kind it was.");
Assert.That(ReadString(block, "thinking"), Is.EqualTo("Weighing the options."), "With the thinking it carried.");
Assert.That(ReadString(block, "signature"), Is.EqualTo("EqQBCgIYAhIM+abc/DEF=="), "And with the signature that was made for exactly that text.");
});
}
}
[Test]
@@ -112,12 +112,12 @@ public sealed class AnthropicMessageStreamAccumulatorTests
"""{"type":"message_stop"}""");
var toolUse = response!.GetToolUses().Single();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(toolUse.Id, Is.EqualTo("toolu_1"), "The ID comes from the block as it opened.");
Assert.That(toolUse.Name, Is.EqualTo("web_search"), "So does the name.");
Assert.That(toolUse.Arguments, Is.EqualTo("""{"query":"weather"}"""), "And the arguments are the fragments joined back together.");
});
}
}
[Test]
@@ -149,11 +149,11 @@ public sealed class AnthropicMessageStreamAccumulatorTests
"""{"type":"message_stop"}""");
var toolUse = response!.GetToolUses().Single();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(toolUse.Arguments, Is.EqualTo("""{"query":"wea"""), "The call carries what actually arrived, which no tool executor will accept.");
Assert.That(ReadRawText(response.Content.Single(), "input"), Is.EqualTo("{}"), "While the block going back to Anthropic carries an object, because anything else would be refused.");
});
}
}
[Test]
@@ -198,11 +198,11 @@ public sealed class AnthropicMessageStreamAccumulatorTests
"""{"type":"content_block_stop","index":0}""",
"""{"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":5}}""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(response, Is.Not.Null, "A message with a stop reason is a message which ended.");
Assert.That(response!.StopReason, Is.EqualTo("end_turn"), "And the reason it ended travels with it.");
});
}
}
[Test]
@@ -232,12 +232,12 @@ public sealed class AnthropicMessageStreamAccumulatorTests
"""{"type":"content_block_stop","index":0}""",
"""{"type":"message_stop"}""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(part.Usage.PromptTokens, Is.EqualTo(2679), "The start of the message states what the request carried.");
Assert.That(part.HasContent, Is.False, "And it has nothing to show.");
Assert.That(response!.Content, Has.Count.EqualTo(1), "Nor does it become a block of the message.");
});
}
}
[Test]
@@ -49,11 +49,11 @@ public sealed class AnthropicToolCallingAdapterTests
var secondRound = await Usages(adapter);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(firstRound, Is.EqualTo(new[] { 1200 }), "The first round states the conversation up to the question.");
Assert.That(secondRound, Is.EqualTo(new[] { 9800 }), "The second round states its own request, and the cumulative number at its end stays behind.");
});
}
}
[Test]
@@ -28,12 +28,12 @@ public sealed class ChatCompletionToolCallAccumulatorTests
"[DONE]");
var call = message!.ToolCalls!.Single()!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(call.Id, Is.EqualTo("call_1"), "The ID arrived with the first fragment and belongs to the whole call.");
Assert.That(call.Function!.Name, Is.EqualTo("web_search"), "So does the name.");
Assert.That(call.Function!.Arguments, Is.EqualTo("""{"query":"weather"}"""), "And the arguments are the fragments in the order they came, joined without anything in between.");
});
}
}
[Test]
@@ -68,11 +68,11 @@ public sealed class ChatCompletionToolCallAccumulatorTests
"""{"choices":[{"index":0,"delta":{"tool_calls":[{"function":{"arguments":"\"weather\"}"}}]}}]}""");
var call = message!.ToolCalls!.Single()!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(call.Id, Is.EqualTo("call_1"), "One call, not two: a fragment without an index belongs to the one being written.");
Assert.That(call.Function!.Arguments, Is.EqualTo("""{"query":"weather"}"""), "And its arguments are complete.");
});
}
}
[Test]
@@ -100,12 +100,12 @@ public sealed class ChatCompletionToolCallAccumulatorTests
.Select(part => part.TextDelta));
var message = accumulator.Build();
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(shown, Is.EqualTo("Let me look that up."), "The text goes out while it is being written, in the pieces it arrives in.");
Assert.That(message!.Content, Is.EqualTo("Let me look that up."), "And the same text goes back to the provider as what the model said.");
Assert.That(message.ToolCalls!.Single()!.Id, Is.EqualTo("call_1"), "The tool call of that round is there as well.");
});
}
}
[Test]
@@ -124,11 +124,11 @@ public sealed class ChatCompletionToolCallAccumulatorTests
"""{"choices":[{"index":0,"delta":{"reasoning_content":"Thinking about it."}}]}""",
"""{"choices":[{"index":0,"delta":{"content":"The answer."}}]}""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(message!.ReasoningContent, Is.EqualTo("Thinking about it."), "Reasoning is kept, because the next request is charged for it.");
Assert.That(message.Content, Is.EqualTo("The answer."), "And it is not mixed into the answer.");
});
}
}
[Test]
@@ -142,11 +142,11 @@ public sealed class ChatCompletionToolCallAccumulatorTests
var withoutAnyFragment = Read("""{"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_1","function":{"name":"get_time"}}]}}]}""");
var withAnEmptyFragment = Read("""{"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_1","function":{"name":"get_time","arguments":""}}]}}]}""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(withoutAnyFragment!.ToolCalls!.Single()!.Function!.Arguments, Is.EqualTo("{}"), "No fragment at all is a call without arguments, not a broken one.");
Assert.That(withAnEmptyFragment!.ToolCalls!.Single()!.Function!.Arguments, Is.EqualTo("{}"), "And neither is the empty fragment some providers send instead.");
});
}
}
[Test]
@@ -158,11 +158,11 @@ public sealed class ChatCompletionToolCallAccumulatorTests
//
var message = Read("""{"choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_1","function":{"name":"web_search","arguments":"{}"}}]}}]}""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(message!.RawContent, Is.Null, "No text means no content field.");
Assert.That(message.Content, Is.Null, "Which is what the adapter reads as an answer without words.");
});
}
}
[Test]
@@ -190,12 +190,12 @@ public sealed class ChatCompletionToolCallAccumulatorTests
var accumulator = new ChatCompletionToolCallAccumulator();
var part = accumulator.Process(Event("""{"choices":[],"usage":{"prompt_tokens":1200,"completion_tokens":345,"total_tokens":1545}}"""));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(part.Usage.IsKnown, Is.True, "The last line of the stream has no choices, and it must not be dropped for that.");
Assert.That(part.Usage.PromptTokens, Is.EqualTo(1200));
Assert.That(part.HasContent, Is.False, "It has nothing to show, though.");
});
}
}
[Test]
@@ -208,11 +208,11 @@ public sealed class ChatCompletionToolCallAccumulatorTests
var accumulator = new ChatCompletionToolCallAccumulator();
var part = accumulator.Process(Event("""{"choices":[{"index":0,"delta":{"content":"Bye"}}],"usage":{"prompt_tokens":1200,"completion_tokens":2}}"""));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(part.TextDelta, Is.EqualTo("Bye"));
Assert.That(part.Usage.PromptTokens, Is.EqualTo(1200));
});
}
}
private static ChatCompletionResponseMessage? Read(params string[] data)
@@ -59,11 +59,11 @@ public sealed class ChatCompletionToolCallingAdapterTests
var secondRound = await Usages(adapter);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(firstRound, Is.EqualTo(new[] { 1200 }), "The first round reports the conversation up to the question.");
Assert.That(secondRound, Is.EqualTo(new[] { 9800 }), "The second round reports its own request, tool result included, and leaves it to the loop to drop.");
});
}
}
[Test]
@@ -116,12 +116,12 @@ public sealed class ChatCompletionToolCallingAdapterTests
var (call, nextRequest) = await AnswerOneCall(MALFORMED_NAME, "{}", "The tool call was invalid.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(call.IsValid, Is.False, "The call is answered as invalid instead of being run.");
Assert.That(nextRequest, Does.Not.Contain(MALFORMED_NAME), "The provider would reject the whole request over this name.");
Assert.That(nextRequest, Does.Contain("\"name\":\"invalid_tool_call\""), "The call itself still goes back, or its result would answer a call the provider does not know.");
});
}
}
[Test]
@@ -134,12 +134,12 @@ public sealed class ChatCompletionToolCallingAdapterTests
//
var (call, nextRequest) = await AnswerOneCall("web_lookup", "{}", "Tool 'web_lookup' is not available.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(call.IsValid, Is.True);
Assert.That(call.ToolName, Is.EqualTo("web_lookup"));
Assert.That(nextRequest, Does.Contain("\"name\":\"web_lookup\""));
});
}
}
[Test]
@@ -153,13 +153,13 @@ public sealed class ChatCompletionToolCallingAdapterTests
var (call, nextRequest) = await AnswerOneCall("web_search", BROKEN_ARGUMENTS, "The tool call was invalid.");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(call.IsValid, Is.False, "The call is answered as invalid instead of being run.");
Assert.That(nextRequest, Does.Not.Contain("weather in Berl"), "The provider would reject the whole request over these arguments.");
Assert.That(nextRequest, Does.Contain("\"arguments\":\"{}\""), "The call itself still goes back, with arguments the provider can read.");
Assert.That(nextRequest, Does.Contain("\"name\":\"web_search\""), "A valid name goes back as it came, even when the arguments next to it are broken.");
});
}
}
/// <summary>
@@ -24,11 +24,11 @@ public sealed class OpenAIStrictToolSchemaTests
.OptionalString("language", "The language.")
.OptionalInteger("page", "The page."))["properties"]!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(Types(properties["language"]!), Is.EqualTo(new[] { "string", "null" }));
Assert.That(Types(properties["page"]!), Is.EqualTo(new[] { "integer", "null" }));
});
}
}
[Test]
@@ -38,11 +38,11 @@ public sealed class OpenAIStrictToolSchemaTests
.RequiredString("query", "The search query.")
.OptionalEnum("time_range", "The time range.", "day", "month", "year"))["properties"]!["time_range"]!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(Types(timeRange), Is.EqualTo(new[] { "string", "null" }));
Assert.That(timeRange["enum"]!.AsArray().Select(value => value?.GetValue<string>()), Is.EqualTo(new[] { null, "day", "month", "year" }));
});
}
}
[Test]
@@ -52,12 +52,12 @@ public sealed class OpenAIStrictToolSchemaTests
.RequiredString("query", "The search query.")
.OptionalStringArray("data_source_ids", "The data sources.", "first", "second"))["properties"]!["data_source_ids"]!;
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(Types(dataSourceIds), Is.EqualTo(["array", "null"]), "Leaving the list out is said by allowing null for the list itself.");
Assert.That(dataSourceIds["items"]!["enum"]!.AsArray().Select(value => value?.GetValue<string>()), Is.EqualTo(["first", "second"]), "Null is a way to leave the list out, not an entry it may hold.");
Assert.That(dataSourceIds["enum"], Is.Null, "The list itself names no values of its own.");
});
}
}
[Test]
@@ -24,11 +24,11 @@ public sealed class ProviderToolAdaptersTests
var definition = WebSearchLike();
var function = Sent(definition, hostEnforcesStrict: false);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(function["strict"]!.GetValue<bool>(), Is.False);
Assert.That(JsonNode.DeepEquals(function["parameters"], JsonNode.Parse(definition.Function.Parameters.GetRawText())), Is.True);
});
}
}
[Test]
@@ -36,11 +36,11 @@ public sealed class ProviderToolAdaptersTests
{
var function = Sent(WebSearchLike(), hostEnforcesStrict: true);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(function["strict"]!.GetValue<bool>(), Is.True);
Assert.That(function["parameters"]!["required"]!.AsArray().Select(name => name!.GetValue<string>()), Is.EqualTo(new[] { "query", "page" }));
});
}
}
[Test]
@@ -49,11 +49,11 @@ public sealed class ProviderToolAdaptersTests
var definition = WebSearchLike(isStrict: false);
var function = Sent(definition, hostEnforcesStrict: true);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(function["strict"]!.GetValue<bool>(), Is.False);
Assert.That(JsonNode.DeepEquals(function["parameters"], JsonNode.Parse(definition.Function.Parameters.GetRawText())), Is.True);
});
}
}
/// <summary>
@@ -38,12 +38,12 @@ public sealed class ResponsesStreamAccumulatorTests
"""{"type":"function_call","call_id":"call_1","name":"web_search","arguments":"{\"query\":\"weather\"}"}""" +
"]}}");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(response!.GetTextOutput(), Is.EqualTo("Here is the answer."), "The text of the round is read out of the completed response.");
Assert.That(response.GetFunctionCalls().Single().CallId, Is.EqualTo("call_1"), "And so are the calls it asked for.");
Assert.That(response.Output, Has.Count.EqualTo(3), "Every output item is kept, because every one of them goes back.");
});
}
}
[Test]
@@ -65,12 +65,12 @@ public sealed class ResponsesStreamAccumulatorTests
{
var accumulator = new ResponsesStreamAccumulator();
var part = accumulator.Process(Event("""{"type":"response.output_text.annotation.added","annotation_index":0,"annotation":{"type":"url_citation","title":"Example","url":"https://example.org/"}}"""));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(part.Sources.Select(x => x.URL), Is.EqualTo(new[] { "https://example.org/" }), "A citation reaches the user as soon as the model makes it.");
Assert.That(part.TextDelta, Is.Empty, "A line which only announces a source carries no text.");
});
}
}
[Test]
@@ -84,12 +84,12 @@ public sealed class ResponsesStreamAccumulatorTests
"""{"type":"response.output_item.done","output_index":0,"item":""" + REASONING_ITEM + "}",
"""{"type":"response.output_item.done","output_index":1,"item":{"type":"function_call","call_id":"call_1","name":"web_search","arguments":"{}"}}""");
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(response, Is.Not.Null, "A round built from its items is still a round.");
Assert.That(response!.Output.First().GetRawText(), Is.EqualTo(REASONING_ITEM), "And the reasoning item is as untouched as it would be in the completed event.");
Assert.That(response.GetFunctionCalls().Single().Name, Is.EqualTo("web_search"), "The call is there to be executed.");
});
}
}
[Test]
@@ -111,13 +111,13 @@ public sealed class ResponsesStreamAccumulatorTests
var accumulator = new ResponsesStreamAccumulator();
var part = accumulator.Process(Event(COMPLETED_PREFIX + REASONING_ITEM + """],"usage":{"input_tokens":2679,"input_tokens_details":{"cached_tokens":0},"output_tokens":510}}}"""));
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(part.Usage.IsKnown, Is.True, "The closing event is the one line which states what the request cost.");
Assert.That(part.Usage.PromptTokens, Is.EqualTo(2679));
Assert.That(part.HasContent, Is.False, "And there is nothing on it to show.");
Assert.That(accumulator.Build()!.Output, Has.Count.EqualTo(1), "Reading the usage leaves the round as it was.");
});
}
}
[Test]
@@ -40,11 +40,11 @@ public sealed class ResponsesToolCallingAdapterTests
var secondRound = await Usages(adapter);
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(firstRound, Is.EqualTo(new[] { 1200 }), "The first round states the conversation up to the question.");
Assert.That(secondRound, Is.EqualTo(new[] { 9800 }), "The second round states its own request.");
});
}
}
[Test]
@@ -70,11 +70,11 @@ public sealed class ResponsesToolCallingAdapterTests
written.Add(delta.Content);
}
Assert.Multiple(() =>
using (Assert.EnterMultipleScope())
{
Assert.That(usages, Is.Empty, "What the search found is part of the number, and no later request carries it.");
Assert.That(written, Is.EqualTo(new[] { "It is sunny." }), "The answer itself goes out as always.");
});
}
}
/// <summary>