Skip to content

Commit 5703fc0

Browse files
committed
fb
1 parent 4009c2b commit 5703fc0

File tree

4 files changed

+53
-89
lines changed

4 files changed

+53
-89
lines changed

src/Utility/ChatTools.cs

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public ChatTools(EmbeddingClient client = null)
4141
public ChatTools(params Type[] tools) : this((EmbeddingClient)null)
4242
{
4343
foreach (var t in tools)
44-
AddLocalTool(t);
44+
AddFunctionTool(t);
4545
}
4646

4747
/// <summary>
@@ -58,27 +58,27 @@ public ChatTools(params Type[] tools) : this((EmbeddingClient)null)
5858
/// Adds local tool implementations from the provided types.
5959
/// </summary>
6060
/// <param name="tools">Types containing static methods to be used as tools.</param>
61-
public void AddLocalTools(params Type[] tools)
61+
public void AddFunctionTools(params Type[] tools)
6262
{
6363
foreach (Type functionHolder in tools)
64-
AddLocalTool(functionHolder);
64+
AddFunctionTool(functionHolder);
6565
}
6666

6767
/// <summary>
6868
/// Adds all public static methods from the specified type as tools.
6969
/// </summary>
7070
/// <param name="tool">The type containing tool methods.</param>
71-
internal void AddLocalTool(Type tool)
71+
internal void AddFunctionTool(Type tool)
7272
{
7373
#pragma warning disable IL2070
7474
foreach (MethodInfo function in tool.GetMethods(BindingFlags.Public | BindingFlags.Static))
7575
{
76-
AddLocalTool(function);
76+
AddFunctionTool(function);
7777
}
7878
#pragma warning restore IL2070
7979
}
8080

81-
internal void AddLocalTool(MethodInfo function)
81+
internal void AddFunctionTool(MethodInfo function)
8282
{
8383
string name = function.Name;
8484
var tool = ChatTool.CreateFunctionTool(name, ToolsUtility.GetMethodDescription(function), ToolsUtility.BuildParametersJson(function.GetParameters()));
@@ -97,7 +97,7 @@ public async Task AddMcpToolsAsync(McpClient client)
9797
_mcpClientsByEndpoint[client.Endpoint.AbsoluteUri] = client;
9898
await client.StartAsync().ConfigureAwait(false);
9999
BinaryData tools = await client.ListToolsAsync().ConfigureAwait(false);
100-
await AddToolsAsync(tools, client).ConfigureAwait(false);
100+
await AddMcpToolsAsync(tools, client).ConfigureAwait(false);
101101
_mcpClients.Add(client);
102102
}
103103

@@ -112,7 +112,7 @@ public async Task AddMcpToolsAsync(Uri mcpEndpoint)
112112
await AddMcpToolsAsync(client).ConfigureAwait(false);
113113
}
114114

115-
private async Task AddToolsAsync(BinaryData toolDefinitions, McpClient client)
115+
private async Task AddMcpToolsAsync(BinaryData toolDefinitions, McpClient client)
116116
{
117117
List<ChatTool> toolsToVectorize = new();
118118
var parsedTools = ToolsUtility.ParseMcpToolDefinitions(toolDefinitions, client);
@@ -158,7 +158,7 @@ private ChatTool ParseToolDefinition(BinaryData data)
158158
/// Converts the tools collection to chat completion options.
159159
/// </summary>
160160
/// <returns>A new ChatCompletionOptions containing all defined tools.</returns>
161-
public ChatCompletionOptions CreateCompletionOptions()
161+
public ChatCompletionOptions ToChatCompletionOptions()
162162
{
163163
var options = new ChatCompletionOptions();
164164
foreach (var tool in _tools)
@@ -173,10 +173,10 @@ public ChatCompletionOptions CreateCompletionOptions()
173173
/// <param name="maxTools">The maximum number of tools to return. Default is 3.</param>
174174
/// <param name="minVectorDistance">The similarity threshold for including tools. Default is 0.29.</param>
175175
/// <returns>A new <see cref="ChatCompletionOptions"/> containing the most relevant tools.</returns>
176-
public ChatCompletionOptions CreateCompletionOptions(string prompt, int maxTools = 3, float minVectorDistance = 0.29f)
176+
public ChatCompletionOptions CreateCompletionOptions(string prompt, int maxTools = 5, float minVectorDistance = 0.29f)
177177
{
178178
if (!CanFilterTools)
179-
return CreateCompletionOptions();
179+
return ToChatCompletionOptions();
180180

181181
var completionOptions = new ChatCompletionOptions();
182182
foreach (var tool in FindRelatedTools(false, prompt, maxTools, minVectorDistance).GetAwaiter().GetResult())
@@ -191,10 +191,10 @@ public ChatCompletionOptions CreateCompletionOptions(string prompt, int maxTools
191191
/// <param name="maxTools">The maximum number of tools to return. Default is 3.</param>
192192
/// <param name="minVectorDistance">The similarity threshold for including tools. Default is 0.29.</param>
193193
/// <returns>A new <see cref="ChatCompletionOptions"/> containing the most relevant tools.</returns>
194-
public async Task<ChatCompletionOptions> CreateCompletionOptionsAsync(string prompt, int maxTools = 3, float minVectorDistance = 0.29f)
194+
public async Task<ChatCompletionOptions> ToChatCompletionOptions(string prompt, int maxTools = 5, float minVectorDistance = 0.29f)
195195
{
196196
if (!CanFilterTools)
197-
return CreateCompletionOptions();
197+
return ToChatCompletionOptions();
198198

199199
var completionOptions = new ChatCompletionOptions();
200200
foreach (var tool in await FindRelatedTools(true, prompt, maxTools, minVectorDistance).ConfigureAwait(false))
@@ -223,12 +223,6 @@ await ToolsUtility.GetEmbeddingAsync(_client, prompt).ConfigureAwait(false) :
223223
}
224224
}
225225

226-
/// <summary>
227-
/// Implicitly converts ChatTools to <see cref="ChatCompletionOptions"/>.
228-
/// </summary>
229-
/// <param name="tools">The ChatTools instance to convert.</param>
230-
public static implicit operator ChatCompletionOptions(ChatTools tools) => tools.CreateCompletionOptions();
231-
232226
internal string CallLocal(ChatToolCall call)
233227
{
234228
var arguments = new List<object>();
@@ -260,6 +254,8 @@ internal async Task<string> CallMcpAsync(ChatToolCall call)
260254
var actualFunctionName = call.FunctionName.Substring(index + ToolsUtility.McpToolSeparator.Length);
261255
#endif
262256
var result = await method(actualFunctionName, call.FunctionArguments).ConfigureAwait(false);
257+
if (result == null)
258+
throw new InvalidOperationException($"MCP tool {call.FunctionName} returned null. Function tools should always return a value.");
263259
return result.ToString();
264260
}
265261

src/Utility/ResponseTools.cs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public ResponseTools(EmbeddingClient client = null)
4141
public ResponseTools(params Type[] tools) : this((EmbeddingClient)null)
4242
{
4343
foreach (var t in tools)
44-
AddLocalTool(t);
44+
AddFunctionTool(t);
4545
}
4646

4747
/// <summary>
@@ -58,27 +58,27 @@ public ResponseTools(params Type[] tools) : this((EmbeddingClient)null)
5858
/// Adds local tool implementations from the provided types.
5959
/// </summary>
6060
/// <param name="tools">Types containing static methods to be used as tools.</param>
61-
public void AddLocalTools(params Type[] tools)
61+
public void AddFunctionTools(params Type[] tools)
6262
{
6363
foreach (Type functionHolder in tools)
64-
AddLocalTool(functionHolder);
64+
AddFunctionTool(functionHolder);
6565
}
6666

6767
/// <summary>
6868
/// Adds all public static methods from the specified type as tools.
6969
/// </summary>
7070
/// <param name="tool">The type containing tool methods.</param>
71-
internal void AddLocalTool(Type tool)
71+
internal void AddFunctionTool(Type tool)
7272
{
7373
#pragma warning disable IL2070
7474
foreach (MethodInfo function in tool.GetMethods(BindingFlags.Public | BindingFlags.Static))
7575
{
76-
AddLocalTool(function);
76+
AddFunctionTool(function);
7777
}
7878
#pragma warning restore IL2070
7979
}
8080

81-
internal void AddLocalTool(MethodInfo function)
81+
internal void AddFunctionTool(MethodInfo function)
8282
{
8383
string name = function.Name;
8484
var tool = ResponseTool.CreateFunctionTool(name, ToolsUtility.GetMethodDescription(function), ToolsUtility.BuildParametersJson(function.GetParameters()));
@@ -97,7 +97,7 @@ public async Task AddMcpToolsAsync(McpClient client)
9797
_mcpClientsByEndpoint[client.Endpoint.AbsoluteUri] = client;
9898
await client.StartAsync().ConfigureAwait(false);
9999
BinaryData tools = await client.ListToolsAsync().ConfigureAwait(false);
100-
await AddToolsAsync(tools, client).ConfigureAwait(false);
100+
await AddMcpToolsAsync(tools, client).ConfigureAwait(false);
101101
_mcpClients.Add(client);
102102
}
103103

@@ -112,7 +112,7 @@ public async Task AddMcpToolsAsync(Uri mcpEndpoint)
112112
await AddMcpToolsAsync(client).ConfigureAwait(false);
113113
}
114114

115-
private async Task AddToolsAsync(BinaryData toolDefinitions, McpClient client)
115+
private async Task AddMcpToolsAsync(BinaryData toolDefinitions, McpClient client)
116116
{
117117
List<ResponseTool> toolsToVectorize = new();
118118
var parsedTools = ToolsUtility.ParseMcpToolDefinitions(toolDefinitions, client);
@@ -161,7 +161,7 @@ private ResponseTool ParseToolDefinition(BinaryData data)
161161
/// Converts the tools collection to <see cref="ResponseCreationOptions"> configured with the tools contained in this instance..
162162
/// </summary>
163163
/// <returns>A new ResponseCreationOptions containing all defined tools.</returns>
164-
public ResponseCreationOptions CreateResponseOptions()
164+
public ResponseCreationOptions ToResponseCreationOptions()
165165
{
166166
var options = new ResponseCreationOptions();
167167
foreach (var tool in _tools)
@@ -176,10 +176,10 @@ public ResponseCreationOptions CreateResponseOptions()
176176
/// <param name="maxTools">The maximum number of tools to return. Default is 5.</param>
177177
/// <param name="minVectorDistance">The similarity threshold for including tools. Default is 0.29.</param>
178178
/// <returns>A new ResponseCreationOptions containing the most relevant tools.</returns>
179-
public ResponseCreationOptions CreateResponseOptions(string prompt, int maxTools = 5, float minVectorDistance = 0.29f)
179+
public ResponseCreationOptions ToResponseCreationOptions(string prompt, int maxTools = 5, float minVectorDistance = 0.29f)
180180
{
181181
if (!CanFilterTools)
182-
return CreateResponseOptions();
182+
return ToResponseCreationOptions();
183183

184184
var completionOptions = new ResponseCreationOptions();
185185
foreach (var tool in FindRelatedTools(false, prompt, maxTools, minVectorDistance).GetAwaiter().GetResult())
@@ -194,10 +194,10 @@ public ResponseCreationOptions CreateResponseOptions(string prompt, int maxTools
194194
/// <param name="maxTools">The maximum number of tools to return. Default is 5.</param>
195195
/// <param name="minVectorDistance">The similarity threshold for including tools. Default is 0.29.</param>
196196
/// <returns>A new ResponseCreationOptions containing the most relevant tools.</returns>
197-
public async Task<ResponseCreationOptions> CreateResponseOptionsAsync(string prompt, int maxTools = 5, float minVectorDistance = 0.29f)
197+
public async Task<ResponseCreationOptions> ToResponseCreationOptionsAsync(string prompt, int maxTools = 5, float minVectorDistance = 0.29f)
198198
{
199199
if (!CanFilterTools)
200-
return CreateResponseOptions();
200+
return ToResponseCreationOptions();
201201

202202
var completionOptions = new ResponseCreationOptions();
203203
foreach (var tool in await FindRelatedTools(true, prompt, maxTools, minVectorDistance).ConfigureAwait(false))
@@ -225,12 +225,6 @@ await ToolsUtility.GetEmbeddingAsync(_client, prompt).ConfigureAwait(false) :
225225
}
226226
}
227227

228-
/// <summary>
229-
/// Implicitly converts ResponseTools to ResponseCreationOptions.
230-
/// </summary>
231-
/// <param name="tools">The ResponseTools instance to convert.</param>
232-
public static implicit operator ResponseCreationOptions(ResponseTools tools) => tools.CreateResponseOptions();
233-
234228
internal string CallLocal(FunctionCallResponseItem call)
235229
{
236230
List<object> arguments = new();

tests/Utility/ChatToolsTests.cs

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
using Moq;
2-
using NUnit.Framework;
3-
using OpenAI.Chat;
4-
using OpenAI.Embeddings;
51
using System;
6-
using System.Collections.Generic;
72
using System.ClientModel;
83
using System.ClientModel.Primitives;
4+
using System.Collections.Generic;
95
using System.IO;
106
using System.Linq;
117
using System.Net.Http;
128
using System.Threading;
139
using System.Threading.Tasks;
10+
using Moq;
11+
using NUnit.Framework;
1412
using OpenAI.Agents;
13+
using OpenAI.Chat;
14+
using OpenAI.Embeddings;
1515

1616
namespace OpenAI.Tests.Utility;
1717

@@ -37,7 +37,7 @@ public void Setup()
3737
public void CanAddLocalTools()
3838
{
3939
var tools = new ChatTools();
40-
tools.AddLocalTools(typeof(TestTools));
40+
tools.AddFunctionTools(typeof(TestTools));
4141

4242
Assert.That(tools.Tools, Has.Count.EqualTo(2));
4343
Assert.That(tools.Tools.Any(t => t.FunctionName == "Echo"));
@@ -48,7 +48,7 @@ public void CanAddLocalTools()
4848
public async Task CanCallToolsAsync()
4949
{
5050
var tools = new ChatTools();
51-
tools.AddLocalTools(typeof(TestTools));
51+
tools.AddFunctionTools(typeof(TestTools));
5252

5353
var toolCalls = new[]
5454
{
@@ -70,9 +70,9 @@ public async Task CanCallToolsAsync()
7070
public void CreatesCompletionOptionsWithTools()
7171
{
7272
var tools = new ChatTools();
73-
tools.AddLocalTools(typeof(TestTools));
73+
tools.AddFunctionTools(typeof(TestTools));
7474

75-
var options = tools.CreateCompletionOptions();
75+
var options = tools.ToChatCompletionOptions();
7676

7777
Assert.That(options.Tools, Has.Count.EqualTo(2));
7878
Assert.That(options.Tools.Any(t => t.FunctionName == "Echo"));
@@ -98,26 +98,13 @@ public async Task CanFilterToolsByRelevance()
9898
.ReturnsAsync(ClientResult.FromValue(embedding, mockResponse));
9999

100100
var tools = new ChatTools(mockEmbeddingClient.Object);
101-
tools.AddLocalTools(typeof(TestTools));
101+
tools.AddFunctionTools(typeof(TestTools));
102102

103-
var options = await tools.CreateCompletionOptionsAsync("Need to add two numbers", 1, 0.5f);
103+
var options = await tools.ToChatCompletionOptions("Need to add two numbers", 1, 0.5f);
104104

105105
Assert.That(options.Tools, Has.Count.LessThanOrEqualTo(1));
106106
}
107107

108-
[Test]
109-
public void ImplicitConversionToCompletionOptions()
110-
{
111-
var tools = new ChatTools();
112-
tools.AddLocalTools(typeof(TestTools));
113-
114-
ChatCompletionOptions options = tools;
115-
116-
Assert.That(options.Tools, Has.Count.EqualTo(2));
117-
Assert.That(options.Tools.Any(t => t.FunctionName == "Echo"));
118-
Assert.That(options.Tools.Any(t => t.FunctionName == "Add"));
119-
}
120-
121108
[Test]
122109
public void ThrowsWhenCallingNonExistentTool()
123110
{
@@ -306,11 +293,11 @@ public async Task CreateCompletionOptions_WithMaxToolsParameter_FiltersTools()
306293

307294
// Act & Assert
308295
// Test with maxTools = 1
309-
var options1 = await tools.CreateCompletionOptionsAsync("calculate 2+2", 1, 0.5f);
296+
var options1 = await tools.ToChatCompletionOptions("calculate 2+2", 1, 0.5f);
310297
Assert.That(options1.Tools, Has.Count.EqualTo(1));
311298

312299
// Test with maxTools = 2
313-
var options2 = await tools.CreateCompletionOptionsAsync("calculate 2+2", 2, 0.5f);
300+
var options2 = await tools.ToChatCompletionOptions("calculate 2+2", 2, 0.5f);
314301
Assert.That(options2.Tools, Has.Count.EqualTo(2));
315302

316303
// Test that we can call the tools after filtering

0 commit comments

Comments
 (0)