Skip to content

Commit d62ff17

Browse files
gunpal5Gunpal Jain
andauthored
fix: Functions without parameters (#31)
* fix: Functions without parameters Co-authored-by: Gunpal Jain <[email protected]>
1 parent 10780c4 commit d62ff17

File tree

7 files changed

+223
-28
lines changed

7 files changed

+223
-28
lines changed

src/libs/CSharpToJsonSchema/SchemaSubsetHelper.cs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ namespace CSharpToJsonSchema;
99

1010
public static class SchemaBuilder
1111
{
12-
13-
public static OpenApiSchema ConvertToSchema(JsonTypeInfo type, string descriptionString)
12+
public static OpenApiSchema? ConvertToSchema(JsonTypeInfo type, string descriptionString)
1413
{
14+
if (type.Properties.Count == 0)
15+
return null;
1516
var typeInfo = type;
1617

1718
var dics = JsonSerializer.Deserialize(descriptionString,
@@ -26,33 +27,35 @@ public static OpenApiSchema ConvertToSchema(JsonTypeInfo type, string descriptio
2627
{
2728
schema["type"] = "string";
2829
}
29-
30+
3031
ExtractDescription(context, schema, dics);
3132
if (context.PropertyInfo == null)
3233
return schema;
33-
34+
3435
return schema;
3536
},
3637
});
37-
38+
3839
var schema = JsonSerializer.Deserialize(x.ToJsonString(), OpenApiSchemaJsonContext.Default.OpenApiSchema);
3940

4041
foreach (var re in schema.Properties)
4142
{
4243
required.Add(re.Key);
4344
}
44-
45+
4546
var mainDescription = schema.Description ?? (dics.TryGetValue("mainFunction_Desc", out var desc) ? desc : "");
46-
return new OpenApiSchema()
47-
{
48-
Description = mainDescription,
49-
Properties = schema.Properties,
50-
Required = required,
51-
Type = "object"
52-
};
47+
return
48+
new OpenApiSchema()
49+
{
50+
Description = mainDescription,
51+
Properties = schema.Properties,
52+
Required = required,
53+
Type = "object"
54+
};
5355
}
54-
55-
private static void ExtractDescription(JsonSchemaExporterContext context, JsonNode schema, IDictionary<string, string> dics)
56+
57+
private static void ExtractDescription(JsonSchemaExporterContext context, JsonNode schema,
58+
IDictionary<string, string> dics)
5659
{
5760
// Determine if a type or property and extract the relevant attribute provider.
5861
ICustomAttributeProvider? attributeProvider = context.PropertyInfo is not null
@@ -76,22 +79,22 @@ private static void ExtractDescription(JsonSchemaExporterContext context, JsonNo
7679
}
7780

7881
FixType(schema);
79-
82+
8083
// Apply description attribute to the generated schema.
8184
if (description is not null)
8285
{
8386
if (schema is not JsonObject jObj)
8487
{
8588
// Handle the case where the schema is a Boolean.
8689
JsonValueKind valueKind = schema.GetValueKind();
87-
90+
8891
schema = jObj = new JsonObject();
8992
if (valueKind is JsonValueKind.False)
9093
{
9194
jObj.Add("not", true);
9295
}
9396
}
94-
97+
9598
jObj.Insert(0, "description", description);
9699
}
97100
}
@@ -100,7 +103,7 @@ private static void FixType(JsonNode schema)
100103
{
101104
// If "type" is an array, remove "null" and collapse if it leaves only one type
102105
var typeValue = schema["type"];
103-
if (typeValue!= null && typeValue is JsonArray array)
106+
if (typeValue != null && typeValue is JsonArray array)
104107
{
105108
if (array.Count == 2)
106109
{
@@ -123,6 +126,7 @@ private static void FixType(JsonNode schema)
123126
}
124127
}
125128
}
129+
126130
public static string ToCamelCase(string str)
127131
{
128132
if (!string.IsNullOrEmpty(str) && str.Length > 1)

src/tests/CSharpToJsonSchema.AotTests/MethodFunctionTools_Tests.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ public async Task Should_SampleFunctionTool_StringAsync()
1010
var value = await tools.CallAsync(nameof(mf.SampleFunctionTool_StringAsync), "{\"input\":\"test\"}");
1111

1212
Assert.Equal(value, "\"Hello world\"");
13-
1413
}
1514

1615
[Fact]
@@ -72,7 +71,20 @@ public async Task Should_SampleFunctionTool_Static_Void()
7271
{
7372
var tools = new Tools([MethodFunctionTools.SampleFunctionTool_Static_Void]);
7473
await tools.CallAsync(nameof(MethodFunctionTools.SampleFunctionTool_Static_Void), "{\"input\":\"test\"}");
75-
74+
}
75+
76+
[Fact]
77+
public async Task Should_SampleFunctionTool_Static_No_Parameters()
78+
{
79+
var tools = new Tools([MethodFunctionTools.SampleFunctionTool_Static_No_Parameters]);
80+
await tools.CallAsync(nameof(MethodFunctionTools.SampleFunctionTool_Static_No_Parameters), "{}");
81+
}
82+
83+
[Fact]
84+
public async Task Should_SampleFunctionTool_No_Parameters()
85+
{
86+
var tools = new Tools([MethodFunctionTools.SampleFunctionTool_No_Parameters]);
87+
await tools.CallAsync(nameof(MethodFunctionTools.SampleFunctionTool_No_Parameters), "{}");
7688
}
7789

7890
}

src/tests/CSharpToJsonSchema.AotTests/Services/MethodFunctionTools.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,15 @@ public static string SampleFunctionTool_Static_String(string input)
5252
{
5353
return "Hello world from string return";
5454
}
55+
56+
[FunctionTool]
57+
public static string SampleFunctionTool_Static_No_Parameters()
58+
{
59+
return "Hello world from string return";
60+
}
61+
[FunctionTool]
62+
public static string SampleFunctionTool_No_Parameters()
63+
{
64+
return "Hello world from string return";
65+
}
5566
}

src/tests/CSharpToJsonSchema.IntegrationTests/MethodFunctionTools.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ public string SampleFunctionTool_Static_String(string input)
5353
{
5454
return "Hello world from string return";
5555
}
56+
57+
5658

5759

5860

src/tests/CSharpToJsonSchema.MeaiTests/Meai_Tests.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,60 @@ public async Task ShouldInvokeTheFunctions()
3737
Console.WriteLine(response.Text);
3838
}
3939

40+
// [TestMethod]
41+
public async Task ShouldInvokeTheFunctions_NoParameters()
42+
{
43+
var key = Environment.GetEnvironmentVariable("OPEN_AI_APIKEY",EnvironmentVariableTarget.User);
44+
if (string.IsNullOrWhiteSpace(key))
45+
return;
46+
47+
var client = new OpenAIClient(new ApiKeyCredential(key));
48+
49+
Microsoft.Extensions.AI.OpenAIChatClient openAiClient = new OpenAIChatClient(client.GetChatClient("gpt-4o-mini"));
50+
51+
var chatClient = new Microsoft.Extensions.AI.FunctionInvokingChatClient(openAiClient);
52+
var chatOptions = new ChatOptions();
53+
54+
var service = new StudentRecordService();
55+
var tools = new Tools([service.GetStudentsListAsync]);
56+
chatOptions.Tools = tools.AsMeaiTools();
57+
58+
var message = new ChatMessage(ChatRole.User, "Get all students records");
59+
var response = await chatClient.GetResponseAsync(message,options:chatOptions).ConfigureAwait(false);
60+
61+
response.Text.Contains("John", StringComparison.InvariantCultureIgnoreCase).Should()
62+
.Be(true);
63+
64+
Console.WriteLine(response.Text);
65+
}
66+
67+
//[TestMethod]
68+
public async Task ShouldInvokeTheFunctions_NoParameters2()
69+
{
70+
var key = Environment.GetEnvironmentVariable("OPEN_AI_APIKEY",EnvironmentVariableTarget.User);
71+
if (string.IsNullOrWhiteSpace(key))
72+
return;
73+
74+
var client = new OpenAIClient(new ApiKeyCredential(key));
75+
76+
Microsoft.Extensions.AI.OpenAIChatClient openAiClient = new OpenAIChatClient(client.GetChatClient("gpt-4o-mini"));
77+
78+
var chatClient = new Microsoft.Extensions.AI.FunctionInvokingChatClient(openAiClient);
79+
var chatOptions = new ChatOptions();
80+
81+
var service = new StudentRecordService();
82+
var tools = new Tools([service.GetStudentsList2]);
83+
chatOptions.Tools = tools.AsMeaiTools();
84+
85+
var message = new ChatMessage(ChatRole.User, "Get all students records");
86+
var response = await chatClient.GetResponseAsync(message,options:chatOptions).ConfigureAwait(false);
87+
88+
response.Text.Contains("John", StringComparison.InvariantCultureIgnoreCase).Should()
89+
.Be(true);
90+
91+
Console.WriteLine(response.Text);
92+
}
93+
4094
//[TestMethod]
4195
public async Task ShouldInvokeTheBookService()
4296
{
@@ -63,6 +117,34 @@ public async Task ShouldInvokeTheBookService()
63117
response.Text.Contains("damdamadum", StringComparison.InvariantCultureIgnoreCase).Should()
64118
.Be(true);
65119

120+
Console.WriteLine(response.Text);
121+
}
122+
// [TestMethod]
123+
public async Task ShouldInvokeTheBookService_NoParameters()
124+
{
125+
var key = Environment.GetEnvironmentVariable("OPEN_AI_APIKEY",EnvironmentVariableTarget.User);
126+
if (string.IsNullOrWhiteSpace(key))
127+
return;
128+
var prompt = "Get list of available books";
129+
130+
var chatClient = new OpenAIClient(new ApiKeyCredential(key)).AsChatClient("gpt-4o-mini").AsBuilder().UseFunctionInvocation().Build();
131+
132+
//Microsoft.Extensions.AI.OpenAIChatClient openAiClient = new OpenAIChatClient(client.GetChatClient("gpt-4o-mini"));
133+
134+
//var chatClient = new GenerativeAIChatClient(Environment.GetEnvironmentVariable("GOOGLE_API_KEY",EnvironmentVariableTarget.User));
135+
//var chatClient = new Microsoft.Extensions.AI.FunctionInvokingChatClient(openAiClient);
136+
var chatOptions = new ChatOptions();
137+
138+
var service = new BookStoreService();
139+
140+
chatOptions.Tools = service.AsMeaiTools();
141+
142+
var message = new ChatMessage(ChatRole.User, prompt);
143+
var response = await chatClient.GetResponseAsync(message,options:chatOptions).ConfigureAwait(false);
144+
145+
response.Text.Contains("Five point someone", StringComparison.InvariantCultureIgnoreCase).Should()
146+
.Be(true);
147+
66148
Console.WriteLine(response.Text);
67149
}
68150
}

src/tests/CSharpToJsonSchema.MeaiTests/Services/BookService.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public interface IBookStoreService
1717

1818
[Description("Get book page content")]
1919
public Task<string> GetBookPageContentAsync([Description("Book Name")] string bookName, [Description("Book Page Number")] int bookPageNumber, CancellationToken cancellationToken = default);
20+
[Description("Get List of Books")]
21+
public Task<List<GetAuthorBook>> GetBooksAsync(CancellationToken cancellationToken = default);
2022

2123
}
2224
public class BookStoreService : IBookStoreService
@@ -35,4 +37,9 @@ public Task<string> GetBookPageContentAsync(string bookName, int bookPageNumber,
3537
{
3638
return Task.FromResult("this is a cool weather out there, and I am stuck at home.");
3739
}
40+
41+
public Task<List<GetAuthorBook>> GetBooksAsync(CancellationToken cancellationToken = default)
42+
{
43+
return Task.FromResult(new List<GetAuthorBook>(){new GetAuthorBook(){Title = "Five point someone", Description = "This book is about 3 college friends"},new GetAuthorBook(){Title = "Two States", Description = "This book is about intercast marriage in India"}});
44+
}
3845
}

0 commit comments

Comments
 (0)