-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathCopilotToolTests.cs
More file actions
170 lines (146 loc) · 6.01 KB
/
Copy pathCopilotToolTests.cs
File metadata and controls
170 lines (146 loc) · 6.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/
using Microsoft.Extensions.AI;
using System.ComponentModel;
using System.Text.Json;
using Xunit;
namespace GitHub.Copilot.Test.Unit;
public class CopilotToolTests
{
[Fact]
public void DefineTool_Sets_Name_Description_And_Copilot_Metadata()
{
var function = CopilotTool.DefineTool(
ReturnsOk,
new CopilotToolOptions
{
OverridesBuiltInTool = true,
SkipPermission = true,
Defer = CopilotToolDefer.Auto
});
Assert.Equal("test_tool", function.Name);
Assert.Equal("Test tool", function.Description);
Assert.True(function.AdditionalProperties.TryGetValue("is_override", out var isOverride));
Assert.True((bool)isOverride!);
Assert.True(function.AdditionalProperties.TryGetValue("skip_permission", out var skipPermission));
Assert.True((bool)skipPermission!);
Assert.True(function.AdditionalProperties.TryGetValue("defer", out var defer));
Assert.Equal(CopilotToolDefer.Auto, defer);
}
[Fact]
public void DefineTool_Omits_Copilot_Metadata_When_Flags_Are_False()
{
var function = CopilotTool.DefineTool(ReturnsOk);
Assert.False(function.AdditionalProperties.ContainsKey("is_override"));
Assert.False(function.AdditionalProperties.ContainsKey("skip_permission"));
Assert.False(function.AdditionalProperties.ContainsKey("defer"));
}
[Fact]
public void DefineTool_Sets_Metadata_In_Additional_Properties()
{
var metadata = new Dictionary<string, object>
{
["github.com/copilot:safeForTelemetry"] = new Dictionary<string, object>
{
["name"] = true,
["inputsNames"] = false
}
};
var function = CopilotTool.DefineTool(
ReturnsOk,
new CopilotToolOptions { Metadata = metadata });
Assert.True(function.AdditionalProperties.TryGetValue("metadata", out var value));
Assert.Same(metadata, value);
}
[Fact]
public void DefineTool_Omits_Metadata_When_Unset()
{
var function = CopilotTool.DefineTool(ReturnsOk);
Assert.False(function.AdditionalProperties.ContainsKey("metadata"));
}
[Fact]
public void DefineTool_Accepts_Lambda_Handlers_Without_Casts()
{
var function = CopilotTool.DefineTool((string value) => value, factoryOptions: new() { Name = "echo", Description = "Echo a value" });
Assert.Equal("echo", function.Name);
}
[Fact]
public async Task DefineTool_Binds_ToolInvocation_And_Excludes_It_From_Schema()
{
var function = CopilotTool.DefineTool(
(string value, ToolInvocation invocation) => $"{value}:{invocation.ToolName}",
factoryOptions: new() { Name = "echo", Description = "Echo a value" });
var schema = function.JsonSchema.GetRawText();
Assert.Contains("\"value\"", schema);
Assert.DoesNotContain("\"invocation\"", schema);
using var document = JsonDocument.Parse("\"hello\"");
var result = await function.InvokeAsync(new AIFunctionArguments
{
["value"] = document.RootElement.Clone(),
Context = new Dictionary<object, object?>
{
[typeof(ToolInvocation)] = new ToolInvocation { ToolName = "echo" }
}
});
Assert.Equal("hello:echo", Assert.IsType<JsonElement>(result).GetString());
}
[Fact]
public async Task DefineTool_Preserves_Custom_Parameter_Binding()
{
var function = CopilotTool.DefineTool(
(string value, string suffix, ToolInvocation invocation) => $"{value}:{suffix}:{invocation.ToolName}",
factoryOptions: new()
{
Name = "echo",
Description = "Echo a value",
ConfigureParameterBinding = pi =>
pi.Name == "suffix"
? new AIFunctionFactoryOptions.ParameterBindingOptions
{
ExcludeFromSchema = true,
BindParameter = static (_, _) => "bound"
}
: default
});
var schema = function.JsonSchema.GetRawText();
Assert.Contains("\"value\"", schema);
Assert.DoesNotContain("\"suffix\"", schema);
Assert.DoesNotContain("\"invocation\"", schema);
using var document = JsonDocument.Parse("\"hello\"");
var result = await function.InvokeAsync(new AIFunctionArguments
{
["value"] = document.RootElement.Clone(),
Context = new Dictionary<object, object?>
{
[typeof(ToolInvocation)] = new ToolInvocation { ToolName = "echo" }
}
});
Assert.Equal("hello:bound:echo", Assert.IsType<JsonElement>(result).GetString());
}
[Fact]
public void DefineTool_Preserves_Additional_Properties_And_ToolOptions_Take_Precedence()
{
var function = CopilotTool.DefineTool(
ReturnsOk,
new CopilotToolOptions
{
SkipPermission = true
},
new AIFunctionFactoryOptions
{
Name = "test_tool",
AdditionalProperties = new Dictionary<string, object?>
{
["custom"] = 42,
["skip_permission"] = false,
}
});
Assert.Equal(42, function.AdditionalProperties["custom"]);
Assert.True(function.AdditionalProperties.TryGetValue("skip_permission", out var skipPermission));
Assert.True((bool)skipPermission!);
}
[DisplayName("test_tool")]
[Description("Test tool")]
private static string ReturnsOk() => "ok";
}