Skip to content

Commit 2c85878

Browse files
committed
Refine parameter binding warnings
1 parent cf9f19f commit 2c85878

3 files changed

Lines changed: 109 additions & 13 deletions

File tree

Lib.GAB.Example/Program.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
using System;
22
using System.Linq;
33
using System.Threading.Tasks;
4-
using Lib.GAB;
54
using Lib.GAB.Tools;
65
using Lib.GAB.Server;
6+
using GabpApi = Lib.GAB.Gabp;
77

88
// Example game/application tools
99
public class GameTools
@@ -56,7 +56,7 @@ static async Task Main(string[] args)
5656
Console.WriteLine("Starting GABP Server Example...");
5757

5858
// Check if we're running under GABS
59-
if (Gabp.IsRunningUnderGabs())
59+
if (GabpApi.IsRunningUnderGabs())
6060
{
6161
Console.WriteLine("\nRunning under GABS - using GABS-aware configuration");
6262
await RunGabsAwareExample();
@@ -82,7 +82,7 @@ static async Task RunGabsAwareExample()
8282
{
8383
// Create server with automatic GABS environment detection
8484
var gameTools = new GameTools();
85-
var server = Gabp.CreateGabsAwareServerWithInstance("Example Game", "1.0.0", gameTools);
85+
var server = GabpApi.CreateGabsAwareServerWithInstance("Example Game", "1.0.0", gameTools);
8686

8787
// Register some event channels
8888
server.Events.RegisterChannel("player/move", "Player movement events");
@@ -132,7 +132,7 @@ static async Task RunTraditionalExample()
132132
{
133133
// Create server with tools from a class instance (traditional way)
134134
var gameTools = new GameTools();
135-
var server = Gabp.CreateServerWithInstance("Example Game", "1.0.0", gameTools, port: 0);
135+
var server = GabpApi.CreateServerWithInstance("Example Game", "1.0.0", gameTools, port: 0);
136136

137137
// Register some event channels
138138
server.Events.RegisterChannel("player/move", "Player movement events");
@@ -179,7 +179,7 @@ static async Task RunExternalConfigExample()
179179

180180
// Create server with external configuration
181181
var gameTools = new GameTools();
182-
var server = Gabp.CreateServerWithInstanceAndExternalConfig(
182+
var server = GabpApi.CreateServerWithInstanceAndExternalConfig(
183183
"Example Game", "1.0.0", gameTools, externalPort, externalToken, gameId);
184184

185185
// Register event channels

Lib.GAB.Tests/ToolRegistryTests.cs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.IO;
4+
using System.Threading.Tasks;
5+
using Lib.GAB;
6+
using Lib.GAB.Tools;
7+
8+
namespace Lib.GAB.Tests;
9+
10+
public class ToolRegistryTests
11+
{
12+
private static readonly object TraceLock = new object();
13+
14+
[Fact]
15+
public async Task MatchesParametersCaseInsensitively()
16+
{
17+
var server = Gabp.CreateSimpleServer("Test App", "1.0.0");
18+
server.Tools.RegisterToolsFromInstance(new ParameterBindingTestTools());
19+
20+
var result = await server.Tools.CallToolAsync("math/add", new { A = 5, B = 3 });
21+
22+
Assert.Equal(8, result);
23+
}
24+
25+
[Fact]
26+
public void CaseInsensitiveMatchesDoNotLogUnrecognizedWarnings()
27+
{
28+
var server = Gabp.CreateSimpleServer("Test App", "1.0.0");
29+
server.Tools.RegisterToolsFromInstance(new ParameterBindingTestTools());
30+
31+
var traceOutput = CaptureTraceOutput(() =>
32+
{
33+
var result = server.Tools.CallToolAsync("math/add", new { A = 5, B = 3 }).GetAwaiter().GetResult();
34+
Assert.Equal(8, result);
35+
});
36+
37+
Assert.Contains("parameter 'A' matched 'a' via case-insensitive fallback", traceOutput);
38+
Assert.Contains("parameter 'B' matched 'b' via case-insensitive fallback", traceOutput);
39+
Assert.DoesNotContain("unrecognized parameter 'A'", traceOutput);
40+
Assert.DoesNotContain("unrecognized parameter 'B'", traceOutput);
41+
}
42+
43+
[Fact]
44+
public void UnknownParametersStillLogWarnings()
45+
{
46+
var server = Gabp.CreateSimpleServer("Test App", "1.0.0");
47+
server.Tools.RegisterToolsFromInstance(new ParameterBindingTestTools());
48+
49+
var traceOutput = CaptureTraceOutput(() =>
50+
{
51+
var result = server.Tools.CallToolAsync("math/add", new { a = 5, b = 3, c = 1 }).GetAwaiter().GetResult();
52+
Assert.Equal(8, result);
53+
});
54+
55+
Assert.Contains("unrecognized parameter 'c'", traceOutput);
56+
}
57+
58+
private static string CaptureTraceOutput(Action action)
59+
{
60+
lock (TraceLock)
61+
{
62+
var previousAutoFlush = Trace.AutoFlush;
63+
using var writer = new StringWriter();
64+
using var listener = new TextWriterTraceListener(writer);
65+
Trace.AutoFlush = true;
66+
Trace.Listeners.Add(listener);
67+
68+
try
69+
{
70+
action();
71+
listener.Flush();
72+
return writer.ToString();
73+
}
74+
finally
75+
{
76+
Trace.Listeners.Remove(listener);
77+
Trace.AutoFlush = previousAutoFlush;
78+
}
79+
}
80+
}
81+
82+
public class ParameterBindingTestTools
83+
{
84+
[Tool("math/add", Description = "Add two numbers")]
85+
public int Add([ToolParameter(Description = "First number")] int a,
86+
[ToolParameter(Description = "Second number")] int b)
87+
{
88+
return a + b;
89+
}
90+
}
91+
}

Lib.GAB/Tools/ToolRegistry.cs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -184,18 +184,23 @@ private object[] ConvertParameters(MethodInfo method, object parameters)
184184
}
185185

186186
// Warn about unrecognized keys that don't match any method parameter
187-
var methodParamNames = new HashSet<string>(methodParams.Select(p => p.Name));
187+
var methodParamNames = methodParams.Select(p => p.Name).ToArray();
188+
var exactMethodParamNames = new HashSet<string>(methodParamNames, StringComparer.Ordinal);
188189
foreach (var key in paramDict.Keys)
189190
{
190-
if (!methodParamNames.Contains(key))
191+
if (exactMethodParamNames.Contains(key))
191192
{
192-
// Find close matches for a helpful warning
193-
var closest = methodParamNames
194-
.Where(n => n.Equals(key, StringComparison.OrdinalIgnoreCase))
195-
.FirstOrDefault();
196-
var hint = closest != null ? $" Did you mean '{closest}'?" : "";
197-
Trace.TraceWarning($"[ToolRegistry] Tool '{method.Name}': unrecognized parameter '{key}'.{hint} Known parameters: [{string.Join(", ", methodParamNames)}]");
193+
continue;
194+
}
195+
196+
var closest = methodParamNames
197+
.FirstOrDefault(n => n.Equals(key, StringComparison.OrdinalIgnoreCase));
198+
if (closest != null)
199+
{
200+
continue;
198201
}
202+
203+
Trace.TraceWarning($"[ToolRegistry] Tool '{method.Name}': unrecognized parameter '{key}'. Known parameters: [{string.Join(", ", methodParamNames)}]");
199204
}
200205

201206
for (int i = 0; i < methodParams.Length; i++)

0 commit comments

Comments
 (0)