mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-04 20:07:20 +00:00
Upgraded to .NET 10 (#1147)
This commit is contained in:
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>
|
||||
|
||||
Reference in new issue
Block a user