diff --git a/Google_GenerativeAI.sln b/Google_GenerativeAI.sln
index 286785a7..8ba281c5 100644
--- a/Google_GenerativeAI.sln
+++ b/Google_GenerativeAI.sln
@@ -48,6 +48,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TwoWayAudioCommunicationWpf
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerativeAI.Live.Tests", "tests\GenerativeAI.Live.Tests\GenerativeAI.Live.Tests.csproj", "{157399AE-E8D1-4306-8B59-0BDEB45AED03}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AotTest", "tests\AotTest\AotTest.csproj", "{5BF6737C-D3E4-4C46-ABBF-73ECAEE128AF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -126,6 +128,10 @@ Global
{157399AE-E8D1-4306-8B59-0BDEB45AED03}.Debug|Any CPU.Build.0 = Debug|Any CPU
{157399AE-E8D1-4306-8B59-0BDEB45AED03}.Release|Any CPU.ActiveCfg = Release|Any CPU
{157399AE-E8D1-4306-8B59-0BDEB45AED03}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5BF6737C-D3E4-4C46-ABBF-73ECAEE128AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5BF6737C-D3E4-4C46-ABBF-73ECAEE128AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5BF6737C-D3E4-4C46-ABBF-73ECAEE128AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5BF6737C-D3E4-4C46-ABBF-73ECAEE128AF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -149,6 +155,7 @@ Global
{ACB3E4E1-F967-45E7-81CD-70A4F7785AED} = {AC161F1D-EC76-48D2-86A3-B52584618D49}
{8DC0FF3E-8B46-41B0-B814-6049FD80C8C3} = {61CC49B3-1325-40EB-95DF-89E18A0D041B}
{157399AE-E8D1-4306-8B59-0BDEB45AED03} = {FCCDE15A-B121-4D6C-BD56-D1B043A26F18}
+ {5BF6737C-D3E4-4C46-ABBF-73ECAEE128AF} = {FCCDE15A-B121-4D6C-BD56-D1B043A26F18}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FFF3E8BB-BACD-4376-8E33-55D6E8A30BE0}
diff --git a/samples/TwoWayAudioCommunicationWpf/TwoWayAudioCommunicationWpf.csproj b/samples/TwoWayAudioCommunicationWpf/TwoWayAudioCommunicationWpf.csproj
index db5b0724..4b42db30 100644
--- a/samples/TwoWayAudioCommunicationWpf/TwoWayAudioCommunicationWpf.csproj
+++ b/samples/TwoWayAudioCommunicationWpf/TwoWayAudioCommunicationWpf.csproj
@@ -7,6 +7,7 @@
enable
true
true
+
diff --git a/src/GenerativeAI.Auth/GenerativeAI.Auth.csproj b/src/GenerativeAI.Auth/GenerativeAI.Auth.csproj
index e7d102c4..d0800048 100644
--- a/src/GenerativeAI.Auth/GenerativeAI.Auth.csproj
+++ b/src/GenerativeAI.Auth/GenerativeAI.Auth.csproj
@@ -21,6 +21,7 @@
True
GenerativeAI.Authenticators
GenerativeAI.Authenticators
+
diff --git a/src/GenerativeAI.Live/GenerativeAI.Live.csproj b/src/GenerativeAI.Live/GenerativeAI.Live.csproj
index 8924f4c6..62595f53 100644
--- a/src/GenerativeAI.Live/GenerativeAI.Live.csproj
+++ b/src/GenerativeAI.Live/GenerativeAI.Live.csproj
@@ -19,6 +19,7 @@
2.3.1
True
True
+ true
diff --git a/src/GenerativeAI.Live/Models/MultiModalLiveClient.cs b/src/GenerativeAI.Live/Models/MultiModalLiveClient.cs
index b4a932e4..896ed4fa 100644
--- a/src/GenerativeAI.Live/Models/MultiModalLiveClient.cs
+++ b/src/GenerativeAI.Live/Models/MultiModalLiveClient.cs
@@ -4,6 +4,7 @@
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Json;
+using System.Text.Json.Serialization.Metadata;
using GenerativeAI.Core;
using GenerativeAI.Live.Helper;
using GenerativeAI.Live.Logging;
@@ -183,11 +184,11 @@ private void ProcessReceivedMessage(ResponseMessage msg)
BidiResponsePayload? responsePayload = null;
if (msg.MessageType == WebSocketMessageType.Binary)
{
- responsePayload = JsonSerializer.Deserialize(msg.Binary);
+ responsePayload = JsonSerializer.Deserialize(msg.Binary,(JsonTypeInfo) DefaultSerializerOptions.Options.GetTypeInfo(typeof(BidiResponsePayload)));
}
else
{
- responsePayload = JsonSerializer.Deserialize(msg.Text);
+ responsePayload = JsonSerializer.Deserialize(msg.Text,(JsonTypeInfo) DefaultSerializerOptions.Options.GetTypeInfo(typeof(BidiResponsePayload)));
}
if (responsePayload == null)
@@ -228,7 +229,7 @@ private void ProcessTextChunk(BidiResponsePayload responsePayload)
{
if (part.Text != null)
{
- this.TextChunkReceived.Invoke(this,
+ this.TextChunkReceived?.Invoke(this,
new TextChunkReceivedArgs(part.Text, responsePayload.ServerContent.TurnComplete == true));
_logger?.LogInformation("Text chunk received: {Text}", part.Text);
}
@@ -565,7 +566,7 @@ private async Task SendAsync(BidiClientPayload payload, CancellationToken cancel
try
{
- var json = JsonSerializer.Serialize(payload,DefaultSerializerOptions.Options);
+ var json = JsonSerializer.Serialize(payload,DefaultSerializerOptions.Options.GetTypeInfo(typeof(BidiClientPayload)));
_logger?.LogMessageSent(json);
_client.Send(json);
diff --git a/src/GenerativeAI.Microsoft/Extensions/MicrosoftExtensions.cs b/src/GenerativeAI.Microsoft/Extensions/MicrosoftExtensions.cs
index 40e08c62..6151d3b7 100644
--- a/src/GenerativeAI.Microsoft/Extensions/MicrosoftExtensions.cs
+++ b/src/GenerativeAI.Microsoft/Extensions/MicrosoftExtensions.cs
@@ -1,5 +1,5 @@
using GenerativeAI.Types;
-using Json.More;
+
using Microsoft.Extensions.AI;
using System.Text.Json;
using System.Text.Json.Nodes;
@@ -70,8 +70,8 @@ where p is not null
/// A object constructed from the provided JSON schema, or null if deserialization fails.
public static Schema? ToSchema(this JsonElement schema)
{
-
- var serialized = JsonSerializer.Serialize(schema);
+ return GoogleSchemaHelper.ConvertToCompatibleSchemaSubset(schema.AsNode().AsObject());
+ var serialized = JsonSerializer.Serialize(schema, DefaultSerializerOptions.Options.GetTypeInfo(schema.GetType()));
return JsonSerializer.Deserialize(serialized,SchemaSourceGenerationContext.Default.Schema);
}
@@ -98,7 +98,7 @@ where p is not null
FunctionCall = new FunctionCall()
{
Name = fcc.Name,
- Args = fcc.Arguments!,
+ Args = fcc.Arguments.ToJsonNode(),
}
},
FunctionResultContent frc => new Part
@@ -106,19 +106,61 @@ where p is not null
FunctionResponse = new FunctionResponse()
{
Name = frc.CallId,
- Response = new
- {
- Name = frc.CallId,
- Content = JsonSerializer.SerializeToNode(frc.Result)!,
- }
+ Response = frc.ToJsonNodeResponse()
}
},
_ => null,
};
}
-
+ private static JsonNode ToJsonNode(this IDictionary? args)
+ {
+ var node = new JsonObject();
+ foreach (var arg in args!)
+ {
+ if (arg.Value is JsonNode nd)
+ node.Add(arg.Key, nd.DeepClone());
+ else
+ {
+ var p = arg.Value switch
+ {
+ string s => s,
+ int i => i,
+ float f => f,
+ double d => d,
+ bool b => b,
+ null => null,
+ JsonElement e => e.AsNode()?.AsObject(),
+ JsonNode n => n switch
+ {
+ JsonObject o => o,
+ JsonArray a => a,
+ JsonValue v => v.GetValue().AsNode()
+ },
+ _ => throw new Exception("Unsupported argument type")
+ };
+ node.Add(arg.Key, p);
+ }
+ }
+ return node; //JsonSerializer.Deserialize(node.ToJsonString(), TypesSerializerContext.Default.JsonElement)!;
+ }
+ public static JsonNode ToJsonNodeResponse(this object? response)
+ {
+ if (response is FunctionResultContent content)
+ {
+ if (content.Result is JsonObject obj)
+ return obj;
+ else if (content.Result is JsonNode arr)
+ return arr;
+ }
+ if(response is JsonNode node)
+ {
+ return node;
+ }
+ else throw new Exception("Response is not a json node");
+
+ }
///
/// Maps into a object used by GenerativeAI.
///
@@ -137,7 +179,7 @@ where p is not null
config.TopK = options.TopK;
config.MaxOutputTokens = options.MaxOutputTokens;
config.StopSequences = options.StopSequences?.ToList();
- config.Seed = (int) options.Seed!;
+ config.Seed = (int?) options.Seed;
config.ResponseMimeType = options.ResponseFormat is ChatResponseFormatJson ? "application/json" : null;
if (options.ResponseFormat is ChatResponseFormatJson jsonFormat)
{
@@ -145,7 +187,7 @@ where p is not null
if (jsonFormat.Schema is JsonElement je && je.ValueKind == JsonValueKind.Object)
{
// Workaround to convert our real json schema to the format Google's api expects
- var forGoogleApi = GoogleSchemaHelper.ConvertToCompatibleSchemaSubset(je.ToJsonDocument());
+ var forGoogleApi = GoogleSchemaHelper.ConvertToCompatibleSchemaSubset(je.AsNode());
config.ResponseSchema = forGoogleApi;
}
}
@@ -396,17 +438,23 @@ public static IList ToAiContents(this List? parts)
/// A dictionary where the keys represent argument names and values represent their corresponding data, or null if conversion is not possible.
private static IDictionary? ConvertFunctionCallArg(object? functionCallArgs)
{
- if (functionCallArgs != null && functionCallArgs is not JsonElement)
+ if (functionCallArgs is JsonElement jsonElement)
{
- functionCallArgs = JsonSerializer.Deserialize(JsonSerializer.Serialize(functionCallArgs));
+ var obj = jsonElement.AsNode().AsObject();
+ return obj?.ToDictionary(s=>s.Key,s=>(object?)s.Value?.DeepClone());
+
}
- if (functionCallArgs is JsonElement jsonElement)
+ if (functionCallArgs is JsonNode jsonElement2)
{
- if (jsonElement.ValueKind == JsonValueKind.Object)
- {
- var obj = JsonObject.Create(jsonElement);
- return obj?.ToDictionary(s=>s.Key,s=>(object?)s.Value);
- }
+ var obj = jsonElement2.AsObject();
+ return obj?.ToDictionary(s=>s.Key,s=>(object?)s.Value?.DeepClone());
+ }
+ else if (functionCallArgs != null && functionCallArgs is not JsonNode)
+ {
+ throw new Exception("Unsupported function call argument type");
+ // #pragma warning disable IL2026, IL3050
+ // functionCallArgs = JsonSerializer.Deserialize(JsonSerializer.Serialize(functionCallArgs));
+ // #pragma warning restore IL2026, IL3050
}
return null;
diff --git a/src/GenerativeAI.Microsoft/GenerativeAI.Microsoft.csproj b/src/GenerativeAI.Microsoft/GenerativeAI.Microsoft.csproj
index 55e0d906..ee4c5ebb 100644
--- a/src/GenerativeAI.Microsoft/GenerativeAI.Microsoft.csproj
+++ b/src/GenerativeAI.Microsoft/GenerativeAI.Microsoft.csproj
@@ -21,6 +21,7 @@
2.3.1
True
True
+ true
diff --git a/src/GenerativeAI.Microsoft/GenerativeAIChatClient.cs b/src/GenerativeAI.Microsoft/GenerativeAIChatClient.cs
index 1406b22b..1c071ba6 100644
--- a/src/GenerativeAI.Microsoft/GenerativeAIChatClient.cs
+++ b/src/GenerativeAI.Microsoft/GenerativeAIChatClient.cs
@@ -1,4 +1,6 @@
using System.Runtime.CompilerServices;
+using System.Text.Json;
+using System.Text.Json.Nodes;
using GenerativeAI.Core;
using GenerativeAI.Exceptions;
using GenerativeAI.Microsoft.Extensions;
@@ -70,15 +72,15 @@ private async Task CallFunctionAsync(GenerateContentRequest reques
var content = response.Candidates?.FirstOrDefault()?.Content;
if (content != null)
contents.Add(content);
+ var responseObject = new JsonObject();
+ responseObject["name"] = functionCall.Name;
+ responseObject["content"] = ((JsonElement)result).AsNode().DeepClone();
+ //responseObject["content"] = result as JsonNode;
var functionResponse = new FunctionResponse()
{
Name = tool.Name,
Id = functionCall.CallId,
- Response = new
- {
- Name = tool.Name,
- Content = result
- }
+ Response = responseObject
};
var funcContent = new Content() { Role = Roles.Function };
funcContent.AddPart(new Part()
diff --git a/src/GenerativeAI.Tools/GenerativeAI.Tools.csproj b/src/GenerativeAI.Tools/GenerativeAI.Tools.csproj
index 219c558a..e16355b4 100644
--- a/src/GenerativeAI.Tools/GenerativeAI.Tools.csproj
+++ b/src/GenerativeAI.Tools/GenerativeAI.Tools.csproj
@@ -22,12 +22,13 @@
2.3.1
True
True
+ true
-
+
diff --git a/src/GenerativeAI.Tools/GenericFunctionTool.cs b/src/GenerativeAI.Tools/GenericFunctionTool.cs
index f2bb194e..48148c13 100644
--- a/src/GenerativeAI.Tools/GenericFunctionTool.cs
+++ b/src/GenerativeAI.Tools/GenericFunctionTool.cs
@@ -1,7 +1,9 @@
-using System.Text.Json.Nodes;
+using System.Text.Json;
+using System.Text.Json.Nodes;
using CSharpToJsonSchema;
using GenerativeAI.Core;
using GenerativeAI.Types;
+
using JsonSerializer = System.Text.Json.JsonSerializer;
using Tool = GenerativeAI.Types.Tool;
@@ -13,7 +15,7 @@ namespace GenerativeAI.Tools;
/// It utilizes the code generation capabilities available in CSharpToJsonSchema for transforming
/// tool definitions into executable formats and managing function invocations.
///
-public class GenericFunctionTool:IFunctionTool
+public class GenericFunctionTool:GoogleFunctionTool
{
///
/// Represents a generic functional tool that enables interaction with a set of tools and their associated functions,
@@ -29,7 +31,7 @@ public GenericFunctionTool(IEnumerable tools, IReadOnly
///
- public Tool AsTool()
+ public override Tool AsTool()
{
return new Tool()
{
@@ -44,30 +46,52 @@ public Tool AsTool()
private Schema? ToSchema(object parameters)
{
- var param = JsonSerializer.Serialize(parameters);
+ var param = JsonSerializer.Serialize(parameters, OpenApiSchemaSourceGenerationContext.Default.OpenApiSchema);
return JsonSerializer.Deserialize(param,SchemaSourceGenerationContext.Default.Schema);
}
///
- public async Task CallAsync(FunctionCall functionCall, CancellationToken cancellationToken = default)
+ public override async Task CallAsync(FunctionCall functionCall, CancellationToken cancellationToken = default)
{
+ #pragma disable warning IL2026, IL3050
if (this.Calls.TryGetValue(functionCall.Name, out var call))
{
- var str = JsonSerializer.Serialize(functionCall.Args);
- var response = await call(str, cancellationToken).ConfigureAwait(false);
+ string? args = null;
+ if (functionCall.Args !=null)
+ {
+ args = functionCall.Args.ToJsonString();
+ }
+ // else if (functionCall.Args is JsonNode jsonNode)
+ // {
+ // args = jsonNode.ToJsonString();
+ // }
+ // else if (functionCall.Args is JsonObject jsonObject)
+ // {
+ // args = jsonObject.ToJsonString();
+ // }
+ else
+ {
+ throw new NotImplementedException();
+ //args = JsonSerializer.Serialize(functionCall.Args, DefaultSerializerOptions.Options.GetTypeInfo());
+ }
+ var response = await call(args, cancellationToken).ConfigureAwait(false);
var node = JsonNode.Parse(response);
+ var responseNode = new JsonObject();
- return new FunctionResponse() { Id = functionCall.Id, Name = functionCall.Name, Response = new {
- Name = functionCall.Name,
- Content = node,
- } };
+ responseNode["name"] = functionCall.Name;
+ responseNode["content"] = node;
+ return new FunctionResponse() { Id = functionCall.Id, Name = functionCall.Name,
+
+ Response = responseNode,
+ };
+#pragma restore warning IL2026, IL3050
}
return null;
}
///
- public bool IsContainFunction(string name)
+ public override bool IsContainFunction(string name)
{
return Tools.Any(s => s.Name == name);
}
diff --git a/src/GenerativeAI.Tools/Helpers/FunctionSchemaHelper.cs b/src/GenerativeAI.Tools/Helpers/FunctionSchemaHelper.cs
new file mode 100644
index 00000000..9b54f054
--- /dev/null
+++ b/src/GenerativeAI.Tools/Helpers/FunctionSchemaHelper.cs
@@ -0,0 +1,87 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Metadata;
+using GenerativeAI.Types;
+
+namespace GenerativeAI.Tools.Helpers;
+
+public static class FunctionSchemaHelper
+{
+ #if NET6_0_OR_GREATER
+ [RequiresUnreferencedCode("Create Schema will perform reflection on the delegate type provided to generate Schema")]
+ #endif
+ public static FunctionDeclaration CreateFunctionDecleration(Delegate func, string? name, string? description)
+ {
+ var parameters = func.Method.GetParameters();
+ Schema parametersSchema = new Schema();
+ var options = DefaultSerializerOptions.GenerateObjectJsonOptions;
+
+ parametersSchema.Properties = new Dictionary();
+ parametersSchema.Required = new List();
+ parametersSchema.Type = "object";
+ foreach (var param in parameters)
+ {
+
+ var type = param.ParameterType;
+ if(type.Name == "CancellationToken")
+ continue;
+ var descriptionsDics = GetDescriptionDic(type);
+ var desc = GetDescription(param);
+ descriptionsDics[param.Name.ToCamelCase()] = desc;
+
+ var schema = GoogleSchemaHelper.ConvertToSchema(type, options, descriptionsDics);
+ schema.Description = desc;
+ parametersSchema.Properties.Add(param.Name.ToCamelCase(), schema);
+ parametersSchema.Required.Add(param.Name.ToCamelCase());
+ }
+
+ var functionDescription = GetDescription(func.Method);
+
+ FunctionDeclaration functionObject = new FunctionDeclaration();
+ functionObject.Description = description ?? functionDescription;
+ functionObject.Parameters = parametersSchema;
+ functionObject.Name = name ?? func.Method.Name;
+
+ return functionObject;
+ }
+
+ public static string GetDescription(ParameterInfo paramInfo)
+ {
+ var attribute = paramInfo.GetCustomAttribute();
+ return attribute?.Description ?? string.Empty;
+ }
+
+ private static Dictionary GetDescriptionDic(Type type, Dictionary? descriptions = null)
+ {
+ descriptions = descriptions ?? new Dictionary();
+ descriptions[type.Name.ToCamelCase()] = GetDescription(type);
+ foreach (var member in type.GetMembers())
+ {
+ var description = GetDescription(member);
+ if (!string.IsNullOrEmpty(description))
+ {
+ descriptions[member.Name.ToCamelCase()] = description;
+ }
+
+ if (member.MemberType == MemberTypes.TypeInfo || member.MemberType == MemberTypes.NestedType)
+ {
+ var nestedType = member as Type;
+ if (nestedType != null)
+ {
+ GetDescriptionDic(nestedType, descriptions);
+ }
+ }
+ }
+
+ return descriptions;
+ }
+
+ private static string GetDescription(MemberInfo member)
+ {
+ var attribute = member.GetCustomAttribute();
+ return attribute?.Description ?? string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/src/GenerativeAI.Tools/OpenApiSchemaSourceGenerationContext.cs b/src/GenerativeAI.Tools/OpenApiSchemaSourceGenerationContext.cs
new file mode 100644
index 00000000..9b2aab67
--- /dev/null
+++ b/src/GenerativeAI.Tools/OpenApiSchemaSourceGenerationContext.cs
@@ -0,0 +1,11 @@
+using System.Text.Json.Serialization;
+using CSharpToJsonSchema;
+
+namespace GenerativeAI.Tools;
+
+[JsonSerializable(typeof(OpenApiSchema))]
+[JsonSourceGenerationOptions(WriteIndented = true)]
+public partial class OpenApiSchemaSourceGenerationContext:JsonSerializerContext
+{
+
+}
\ No newline at end of file
diff --git a/src/GenerativeAI.Tools/QuickTool.cs b/src/GenerativeAI.Tools/QuickTool.cs
new file mode 100644
index 00000000..c5617fb8
--- /dev/null
+++ b/src/GenerativeAI.Tools/QuickTool.cs
@@ -0,0 +1,176 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Metadata;
+using GenerativeAI.Core;
+using GenerativeAI.Tools.Helpers;
+using GenerativeAI.Types;
+
+namespace GenerativeAI.Tools;
+
+///
+/// Quick Function Tool,
+///
+///
+/// This class usages reflection and is not compatible with AOT
+///
+public class QuickTool : GoogleFunctionTool
+{
+ ///
+ /// Represents the declaration of a function used within the tool.
+ ///
+ ///
+ /// This property contains metadata about a function, such as its name,
+ /// description, and parameter schema. It's primarily used for defining
+ /// and managing functions available to tools in the framework.
+ ///
+ public FunctionDeclaration FunctionDeclaration { get; private set; }
+ private Delegate _func;
+
+#if NET6_0_OR_GREATER
+ [RequiresUnreferencedCode("QuickTool usages reflection to generate function schema and function invokation. Use GenericFunctionTool for NativeAOT and Trimming support.")]
+#endif
+ ///
+ /// Represents a tool capable of leveraging a delegate function as its core functionality.
+ ///
+ ///
+ /// QuickTool is designed to work with a provided delegate, exposing its functionality as a tool.
+ /// Note that this class utilizes runtime reflection and is not compatible with Ahead-Of-Time (AOT) compilation.
+ ///
+ public QuickTool(Delegate func, string? name = null, string? description = null)
+ {
+ this._func = func;
+ this.FunctionDeclaration = FunctionSchemaHelper.CreateFunctionDecleration(func, name, description);
+ }
+
+ ///
+ public override Tool AsTool()
+ {
+ return new Tool()
+ {
+ FunctionDeclarations = new List() { FunctionDeclaration }
+ };
+ }
+
+ ///
+ public override async Task CallAsync(FunctionCall functionCall,
+ CancellationToken cancellationToken = default)
+ {
+ if (FunctionDeclaration.Name != functionCall.Name)
+ throw new ArgumentException("Function name does not match");
+ object?[]? param = MarshalParameters(functionCall.Args, cancellationToken);
+
+ var result = await InvokeAsTaskAsync(_func, param);
+ var responseNode = new JsonObject();
+ responseNode["name"] = functionCall.Name;
+
+ if (result != null)
+ {
+ var x = JsonSerializer.Serialize(result, DefaultSerializerOptions.GenerateObjectJsonOptions);
+ var node = JsonNode.Parse(x);
+ responseNode["content"] = node;
+ }
+ else
+ {
+ responseNode["content"] = string.Empty;
+ }
+ return new FunctionResponse()
+ {
+ Id = functionCall.Id,
+ Name = functionCall.Name,
+ Response = responseNode
+ };;
+ }
+
+#if NET6_0_OR_GREATER
+ [RequiresUnreferencedCode("The constructor will perform reflection on the delegate type provided")]
+#endif
+ private async Task
/// See Official API Documentation
-[JsonConverter(typeof(JsonStringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum DynamicRetrievalMode // Renamed to DynamicRetrievalMode
{
///
diff --git a/src/GenerativeAI/Types/Embeddings/TaskType.cs b/src/GenerativeAI/Types/Embeddings/TaskType.cs
index 36883860..94d474b7 100644
--- a/src/GenerativeAI/Types/Embeddings/TaskType.cs
+++ b/src/GenerativeAI/Types/Embeddings/TaskType.cs
@@ -6,7 +6,7 @@ namespace GenerativeAI.Types;
/// Specifies the type of task for which the embedding will be used.
///
/// See Official API Documentation
-[JsonConverter(typeof(JsonStringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum TaskType
{
///
diff --git a/src/GenerativeAI/Types/Files/Source.cs b/src/GenerativeAI/Types/Files/Source.cs
index fbcb7a22..bb273284 100644
--- a/src/GenerativeAI/Types/Files/Source.cs
+++ b/src/GenerativeAI/Types/Files/Source.cs
@@ -6,7 +6,7 @@ namespace GenerativeAI.Types;
/// Source of the File.
///
/// See Official API Documentation
-[JsonConverter(typeof(JsonStringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Source
{
///
diff --git a/src/GenerativeAI/Types/Files/State.cs b/src/GenerativeAI/Types/Files/State.cs
index 8f66d38e..5ecc89e4 100644
--- a/src/GenerativeAI/Types/Files/State.cs
+++ b/src/GenerativeAI/Types/Files/State.cs
@@ -6,7 +6,7 @@ namespace GenerativeAI.Types;
/// States for the lifecycle of a File.
///
/// See Official API Documentation
-[JsonConverter(typeof(JsonStringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum FileState
{
///
diff --git a/src/GenerativeAI/Types/Files/Status.cs b/src/GenerativeAI/Types/Files/Status.cs
index 8a2b8913..80b2e0d2 100644
--- a/src/GenerativeAI/Types/Files/Status.cs
+++ b/src/GenerativeAI/Types/Files/Status.cs
@@ -1,4 +1,5 @@
-using System.Text.Json.Serialization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
namespace GenerativeAI.Types;
@@ -35,5 +36,5 @@ public class Status
/// Example: { "id": 1234, "@type": "types.example.com/standard/id" }.
///
[JsonPropertyName("details")]
- public List>? Details { get; set; }
+ public List>? Details { get; set; }
}
\ No newline at end of file
diff --git a/src/GenerativeAI/Types/Imagen/PersonGeneration.cs b/src/GenerativeAI/Types/Imagen/PersonGeneration.cs
index f5a6accc..0c30566e 100644
--- a/src/GenerativeAI/Types/Imagen/PersonGeneration.cs
+++ b/src/GenerativeAI/Types/Imagen/PersonGeneration.cs
@@ -4,6 +4,7 @@ namespace GenerativeAI.Types;
/// Represents the allowed generation of people by the model.
///
/// See Official API Documentation
+[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
public enum PersonGeneration
{
///
diff --git a/src/GenerativeAI/Types/Imagen/SafetySetting.cs b/src/GenerativeAI/Types/Imagen/SafetySetting.cs
index c9b61ecf..52fce338 100644
--- a/src/GenerativeAI/Types/Imagen/SafetySetting.cs
+++ b/src/GenerativeAI/Types/Imagen/SafetySetting.cs
@@ -1,9 +1,12 @@
+using System.Text.Json.Serialization;
+
namespace GenerativeAI.Types;
///
/// Represents the safety filter level.
///
/// See Official API Documentation
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ImageSafetySetting
{
///
diff --git a/src/GenerativeAI/Types/RagEngine/CorpusStatus.cs b/src/GenerativeAI/Types/RagEngine/CorpusStatus.cs
index 2c0837b9..91927cb1 100644
--- a/src/GenerativeAI/Types/RagEngine/CorpusStatus.cs
+++ b/src/GenerativeAI/Types/RagEngine/CorpusStatus.cs
@@ -17,6 +17,6 @@ public class CorpusStatus
/// Output only. RagCorpus life state.
///
[JsonPropertyName("state")]
- [JsonConverter(typeof(JsonStringEnumConverter))]
+
public CorpusStatusState? State { get; set; }
}
\ No newline at end of file
diff --git a/src/GenerativeAI/Types/RagEngine/CorpusStatusState.cs b/src/GenerativeAI/Types/RagEngine/CorpusStatusState.cs
index f5368938..2d49e1f0 100644
--- a/src/GenerativeAI/Types/RagEngine/CorpusStatusState.cs
+++ b/src/GenerativeAI/Types/RagEngine/CorpusStatusState.cs
@@ -1,7 +1,9 @@
using System.Runtime.Serialization;
+using System.Text.Json.Serialization;
namespace GenerativeAI.Types.RagEngine;
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum CorpusStatusState
{
[EnumMember(Value = @"UNKNOWN")]
diff --git a/src/GenerativeAI/Types/RagEngine/FileStatus.cs b/src/GenerativeAI/Types/RagEngine/FileStatus.cs
index 05422404..f0b0e343 100644
--- a/src/GenerativeAI/Types/RagEngine/FileStatus.cs
+++ b/src/GenerativeAI/Types/RagEngine/FileStatus.cs
@@ -17,6 +17,6 @@ public class FileStatus
/// Output only. RagFile state.
///
[JsonPropertyName("state")]
- [JsonConverter(typeof(JsonStringEnumConverter))]
+
public FileStatusState? State { get; set; }
}
\ No newline at end of file
diff --git a/src/GenerativeAI/Types/RagEngine/FileStatusState.cs b/src/GenerativeAI/Types/RagEngine/FileStatusState.cs
index 69ea283f..8e2dfd43 100644
--- a/src/GenerativeAI/Types/RagEngine/FileStatusState.cs
+++ b/src/GenerativeAI/Types/RagEngine/FileStatusState.cs
@@ -1,7 +1,9 @@
using System.Runtime.Serialization;
+using System.Text.Json.Serialization;
namespace GenerativeAI.Types.RagEngine;
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum FileStatusState
{
diff --git a/src/GenerativeAI/Types/RagEngine/GoogleDriveSourceResourceId.cs b/src/GenerativeAI/Types/RagEngine/GoogleDriveSourceResourceId.cs
index 975b49bb..ad2cb0fe 100644
--- a/src/GenerativeAI/Types/RagEngine/GoogleDriveSourceResourceId.cs
+++ b/src/GenerativeAI/Types/RagEngine/GoogleDriveSourceResourceId.cs
@@ -17,6 +17,6 @@ public class GoogleDriveSourceResourceId
/// Required. The type of the Google Drive resource.
///
[JsonPropertyName("resourceType")]
- [JsonConverter(typeof(JsonStringEnumConverter))]
+
public GoogleDriveSourceResourceIdResourceType? ResourceType { get; set; }
}
\ No newline at end of file
diff --git a/src/GenerativeAI/Types/RagEngine/GoogleDriveSourceResourceIdResourceType.cs b/src/GenerativeAI/Types/RagEngine/GoogleDriveSourceResourceIdResourceType.cs
index e9d38513..5eecda72 100644
--- a/src/GenerativeAI/Types/RagEngine/GoogleDriveSourceResourceIdResourceType.cs
+++ b/src/GenerativeAI/Types/RagEngine/GoogleDriveSourceResourceIdResourceType.cs
@@ -1,7 +1,9 @@
using System.Runtime.Serialization;
+using System.Text.Json.Serialization;
namespace GenerativeAI.Types.RagEngine;
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum GoogleDriveSourceResourceIdResourceType
{
diff --git a/src/GenerativeAI/Types/RagEngine/GoogleRpcStatus.cs b/src/GenerativeAI/Types/RagEngine/GoogleRpcStatus.cs
index dbe66df5..d57c9a36 100644
--- a/src/GenerativeAI/Types/RagEngine/GoogleRpcStatus.cs
+++ b/src/GenerativeAI/Types/RagEngine/GoogleRpcStatus.cs
@@ -1,4 +1,5 @@
-using System.Text.Json.Serialization;
+using System.Text.Json;
+using System.Text.Json.Serialization;
namespace GenerativeAI.Types.RagEngine;
@@ -17,7 +18,7 @@ public class GoogleRpcStatus
/// A list of messages that carry the error details. There is a common set of message types for APIs to use.
///
[JsonPropertyName("details")]
- public System.Collections.Generic.ICollection>? Details { get; set; }
+ public System.Collections.Generic.ICollection>? Details { get; set; }
///
/// A developer-facing error message, which should be in English. Any user-facing error message should be localized and sent in the google.rpc.Status.details field, or localized by the client.
diff --git a/src/GenerativeAI/Types/RagEngine/RagFile.cs b/src/GenerativeAI/Types/RagEngine/RagFile.cs
index abf9e8c5..81c5d923 100644
--- a/src/GenerativeAI/Types/RagEngine/RagFile.cs
+++ b/src/GenerativeAI/Types/RagEngine/RagFile.cs
@@ -65,7 +65,7 @@ public class RagFile
/// Output only. The type of the RagFile.
///
[JsonPropertyName("ragFileType")]
- [JsonConverter(typeof(JsonStringEnumConverter))]
+
public RagFileType? RagFileType { get; set; }
///
diff --git a/src/GenerativeAI/Types/RagEngine/RagFileType.cs b/src/GenerativeAI/Types/RagEngine/RagFileType.cs
index 6ba80ed1..19427700 100644
--- a/src/GenerativeAI/Types/RagEngine/RagFileType.cs
+++ b/src/GenerativeAI/Types/RagEngine/RagFileType.cs
@@ -1,7 +1,9 @@
using System.Runtime.Serialization;
+using System.Text.Json.Serialization;
namespace GenerativeAI.Types.RagEngine;
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum RagFileType
{
diff --git a/src/GenerativeAI/Types/SemanticRetrieval/Chunks/State.cs b/src/GenerativeAI/Types/SemanticRetrieval/Chunks/State.cs
index 319c03ab..8812aa68 100644
--- a/src/GenerativeAI/Types/SemanticRetrieval/Chunks/State.cs
+++ b/src/GenerativeAI/Types/SemanticRetrieval/Chunks/State.cs
@@ -6,7 +6,7 @@ namespace GenerativeAI.Types;
/// States for the lifecycle of a .
///
/// See Official API Documentation
-[JsonConverter(typeof(JsonStringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum ChunkState
{
///
diff --git a/src/GenerativeAI/Types/SemanticRetrieval/Corpus/Operator.cs b/src/GenerativeAI/Types/SemanticRetrieval/Corpus/Operator.cs
index c3f2025a..bde08070 100644
--- a/src/GenerativeAI/Types/SemanticRetrieval/Corpus/Operator.cs
+++ b/src/GenerativeAI/Types/SemanticRetrieval/Corpus/Operator.cs
@@ -6,7 +6,7 @@ namespace GenerativeAI.Types;
/// Defines the valid operators that can be applied to a key-value pair.
///
/// See Official API Documentation
-[JsonConverter(typeof(JsonStringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Operator
{
///
diff --git a/src/GenerativeAI/Types/SemanticRetrieval/Permissions/GranteeType.cs b/src/GenerativeAI/Types/SemanticRetrieval/Permissions/GranteeType.cs
index 6b526407..50470752 100644
--- a/src/GenerativeAI/Types/SemanticRetrieval/Permissions/GranteeType.cs
+++ b/src/GenerativeAI/Types/SemanticRetrieval/Permissions/GranteeType.cs
@@ -6,7 +6,7 @@ namespace GenerativeAI.Types;
/// Defines types of the grantee of this permission.
/// See Official API Documentation
///
-[JsonConverter(typeof(JsonStringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum GranteeType
{
///
diff --git a/src/GenerativeAI/Types/SemanticRetrieval/Permissions/Role.cs b/src/GenerativeAI/Types/SemanticRetrieval/Permissions/Role.cs
index 0430c5d0..b091c6e1 100644
--- a/src/GenerativeAI/Types/SemanticRetrieval/Permissions/Role.cs
+++ b/src/GenerativeAI/Types/SemanticRetrieval/Permissions/Role.cs
@@ -6,7 +6,7 @@ namespace GenerativeAI.Types;
/// Defines the role granted by this permission.
/// See Official API Documentation
///
-[JsonConverter(typeof(JsonStringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Role
{
///
diff --git a/src/GenerativeAI/Types/SemanticRetrieval/QuestionAnswering/AnswerStyle.cs b/src/GenerativeAI/Types/SemanticRetrieval/QuestionAnswering/AnswerStyle.cs
index fdba63a7..642c5aa2 100644
--- a/src/GenerativeAI/Types/SemanticRetrieval/QuestionAnswering/AnswerStyle.cs
+++ b/src/GenerativeAI/Types/SemanticRetrieval/QuestionAnswering/AnswerStyle.cs
@@ -6,7 +6,7 @@ namespace GenerativeAI.Types;
/// Style for grounded answers.
///
/// See Official API Documentation
-[JsonConverter(typeof(JsonStringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum AnswerStyle
{
///
diff --git a/src/GenerativeAI/Types/Tuning/TuningState.cs b/src/GenerativeAI/Types/Tuning/TuningState.cs
index e5acd55b..d87b7948 100644
--- a/src/GenerativeAI/Types/Tuning/TuningState.cs
+++ b/src/GenerativeAI/Types/Tuning/TuningState.cs
@@ -6,7 +6,7 @@ namespace GenerativeAI.Types;
/// The state of the tuned model.
///
/// See Official API Documentation
-[JsonConverter(typeof(JsonStringEnumConverter))]
+[JsonConverter(typeof(JsonStringEnumConverter))]
public enum TuningState
{
///
diff --git a/src/GenerativeAI/Types/TypesSerializerContext.cs b/src/GenerativeAI/Types/TypesSerializerContext.cs
new file mode 100644
index 00000000..51e880da
--- /dev/null
+++ b/src/GenerativeAI/Types/TypesSerializerContext.cs
@@ -0,0 +1,220 @@
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
+using GenerativeAI.Core;
+using GenerativeAI.Types.RagEngine;
+
+namespace GenerativeAI.Types;
+
+[JsonSerializable(typeof(CachedContent))]
+[JsonSerializable(typeof(ListCachedContentsResponse))]
+[JsonSerializable(typeof(Duration))]
+[JsonSerializable(typeof(DurationJsonConverter))]
+[JsonSerializable(typeof(Timestamp))]
+[JsonSerializable(typeof(TimestampJsonConverter))]
+[JsonSerializable(typeof(CitationMetadata))]
+[JsonSerializable(typeof(CitationSource))]
+[JsonSerializable(typeof(Schema))]
+[JsonSerializable(typeof(GenerationConfig))]
+[JsonSerializable(typeof(PrebuiltVoiceConfig))]
+[JsonSerializable(typeof(SpeechConfig))]
+[JsonSerializable(typeof(VoiceConfig))]
+[JsonSerializable(typeof(AttributionSourceId))]
+[JsonSerializable(typeof(GroundingAttribution))]
+[JsonSerializable(typeof(GroundingChunk))]
+[JsonSerializable(typeof(GroundingMetadata))]
+[JsonSerializable(typeof(GroundingPassage))]
+[JsonSerializable(typeof(GroundingPassageId))]
+[JsonSerializable(typeof(GroundingPassages))]
+[JsonSerializable(typeof(GroundingSource))]
+[JsonSerializable(typeof(GroundingSupport))]
+[JsonSerializable(typeof(RetrievalMetadata))]
+[JsonSerializable(typeof(SearchEntryPoint))]
+[JsonSerializable(typeof(Segment))]
+[JsonSerializable(typeof(SemanticRetrieverChunk))]
+[JsonSerializable(typeof(Web))]
+[JsonSerializable(typeof(Blob))]
+[JsonSerializable(typeof(Content))]
+[JsonSerializable(typeof(FileData))]
+[JsonSerializable(typeof(Part))]
+[JsonSerializable(typeof(Candidate))]
+[JsonSerializable(typeof(LogprobsCandidate))]
+[JsonSerializable(typeof(LogprobsResult))]
+[JsonSerializable(typeof(ModalityTokenCount))]
+[JsonSerializable(typeof(PromptFeedback))]
+[JsonSerializable(typeof(TopCandidates))]
+[JsonSerializable(typeof(UsageMetadata))]
+[JsonSerializable(typeof(CountTokensRequest))]
+[JsonSerializable(typeof(GenerateContentRequest))]
+[JsonSerializable(typeof(GenerateContentRequestForCountToken))]
+[JsonSerializable(typeof(CountTokensResponse))]
+[JsonSerializable(typeof(GenerateContentResponse))]
+[JsonSerializable(typeof(SafetyRating))]
+[JsonSerializable(typeof(SafetySetting))]
+[JsonSerializable(typeof(Tool))]
+[JsonSerializable(typeof(ToolConfig))]
+[JsonSerializable(typeof(VertexRetrievalTool))]
+[JsonSerializable(typeof(CodeExecutionResult))]
+[JsonSerializable(typeof(CodeExecutionTool))]
+[JsonSerializable(typeof(ExecutableCode))]
+[JsonSerializable(typeof(FunctionCall))]
+[JsonSerializable(typeof(FunctionCallingConfig))]
+[JsonSerializable(typeof(FunctionDeclaration))]
+[JsonSerializable(typeof(FunctionResponse))]
+[JsonSerializable(typeof(GoogleSearchTool))]
+[JsonSerializable(typeof(DynamicRetrievalConfig))]
+[JsonSerializable(typeof(GoogleSearchRetrievalTool))]
+[JsonSerializable(typeof(BatchEmbedContentRequest))]
+[JsonSerializable(typeof(BatchEmbedContentsResponse))]
+[JsonSerializable(typeof(ContentEmbedding))]
+[JsonSerializable(typeof(EmbedContentRequest))]
+[JsonSerializable(typeof(EmbedContentResponse))]
+[JsonSerializable(typeof(RemoteFile))]
+[JsonSerializable(typeof(ListFilesResponse))]
+[JsonSerializable(typeof(Status))]
+[JsonSerializable(typeof(UploadFileRequest))]
+[JsonSerializable(typeof(UploadFileInformation))]
+[JsonSerializable(typeof(UploadFileResponse))]
+[JsonSerializable(typeof(VideoMetadata))]
+[JsonSerializable(typeof(GenerateImageRequest))]
+[JsonSerializable(typeof(GenerateImageResponse))]
+[JsonSerializable(typeof(ImageCaptioningParameters))]
+[JsonSerializable(typeof(ImageCaptioningRequest))]
+[JsonSerializable(typeof(ImageCaptioningResponse))]
+[JsonSerializable(typeof(ImageData))]
+[JsonSerializable(typeof(ImageGenerationInstance))]
+[JsonSerializable(typeof(ImageGenerationParameters))]
+[JsonSerializable(typeof(ImageInstance))]
+[JsonSerializable(typeof(ImageSource))]
+[JsonSerializable(typeof(OutputOptions))]
+[JsonSerializable(typeof(SafetyAttributes))]
+[JsonSerializable(typeof(UpscaleConfig))]
+[JsonSerializable(typeof(VisionGenerativeModelResult))]
+[JsonSerializable(typeof(VqaImage))]
+[JsonSerializable(typeof(VqaInstance))]
+[JsonSerializable(typeof(VqaParameters))]
+[JsonSerializable(typeof(VqaRequest))]
+[JsonSerializable(typeof(VqaResponse))]
+[JsonSerializable(typeof(ListModelsResponse))]
+[JsonSerializable(typeof(Model))]
+[JsonSerializable(typeof(BidiClientPayload))]
+[JsonSerializable(typeof(BidiGenerateContentClientContent))]
+[JsonSerializable(typeof(BidiGenerateContentRealtimeInput))]
+[JsonSerializable(typeof(BidiGenerateContentServerContent))]
+[JsonSerializable(typeof(BidiGenerateContentSetup))]
+[JsonSerializable(typeof(BidiGenerateContentSetupComplete))]
+[JsonSerializable(typeof(BidiGenerateContentToolCall))]
+[JsonSerializable(typeof(BidiGenerateContentToolCallCancellation))]
+[JsonSerializable(typeof(BidiGenerateContentToolResponse))]
+[JsonSerializable(typeof(BidiResponsePayload))]
+[JsonSerializable(typeof(GoogleLongRunningListOperationsResponse))]
+[JsonSerializable(typeof(GoogleLongRunningOperation))]
+[JsonSerializable(typeof(ApiAuth))]
+[JsonSerializable(typeof(ApiAuthApiKeyConfig))]
+[JsonSerializable(typeof(BigQueryDestination))]
+[JsonSerializable(typeof(BigQuerySource))]
+[JsonSerializable(typeof(CorpusStatus))]
+[JsonSerializable(typeof(DirectUploadSource))]
+[JsonSerializable(typeof(FileStatus))]
+[JsonSerializable(typeof(GcsSource))]
+[JsonSerializable(typeof(GoogleDriveSource))]
+[JsonSerializable(typeof(GoogleDriveSourceResourceId))]
+[JsonSerializable(typeof(GoogleRpcStatus))]
+[JsonSerializable(typeof(ImportRagFilesConfig))]
+[JsonSerializable(typeof(ImportRagFilesRequest))]
+[JsonSerializable(typeof(JiraSource))]
+[JsonSerializable(typeof(JiraSourceJiraQueries))]
+[JsonSerializable(typeof(ListRagCorporaResponse))]
+[JsonSerializable(typeof(ListRagFilesResponse))]
+[JsonSerializable(typeof(RagContexts))]
+[JsonSerializable(typeof(RagContextsContext))]
+[JsonSerializable(typeof(RagCorpus))]
+[JsonSerializable(typeof(RagEmbeddingModelConfig))]
+[JsonSerializable(typeof(RagEmbeddingModelConfigHybridSearchConfig))]
+[JsonSerializable(typeof(RagEmbeddingModelConfigSparseEmbeddingConfig))]
+[JsonSerializable(typeof(RagEmbeddingModelConfigSparseEmbeddingConfigBm25))]
+[JsonSerializable(typeof(RagEmbeddingModelConfigVertexPredictionEndpoint))]
+[JsonSerializable(typeof(RagFile))]
+[JsonSerializable(typeof(RagFileChunkingConfig))]
+[JsonSerializable(typeof(RagFileChunkingConfigFixedLengthChunking))]
+[JsonSerializable(typeof(RagFileParsingConfig))]
+[JsonSerializable(typeof(RagFileParsingConfigAdvancedParser))]
+[JsonSerializable(typeof(RagFileParsingConfigLayoutParser))]
+[JsonSerializable(typeof(RagFileParsingConfigLlmParser))]
+[JsonSerializable(typeof(RagFileTransformationConfig))]
+[JsonSerializable(typeof(RagQuery))]
+[JsonSerializable(typeof(RagQueryRanking))]
+[JsonSerializable(typeof(RagRetrievalConfig))]
+[JsonSerializable(typeof(RagRetrievalConfigFilter))]
+[JsonSerializable(typeof(RagRetrievalConfigHybridSearch))]
+[JsonSerializable(typeof(RagRetrievalConfigRanking))]
+[JsonSerializable(typeof(RagRetrievalConfigRankingLlmRanker))]
+[JsonSerializable(typeof(RagRetrievalConfigRankingRankService))]
+[JsonSerializable(typeof(RagVectorDbConfig))]
+[JsonSerializable(typeof(RagVectorDbConfigPinecone))]
+[JsonSerializable(typeof(RagVectorDbConfigRagManagedDb))]
+[JsonSerializable(typeof(RagVectorDbConfigVertexFeatureStore))]
+[JsonSerializable(typeof(RagVectorDbConfigVertexVectorSearch))]
+[JsonSerializable(typeof(RagVectorDbConfigWeaviate))]
+[JsonSerializable(typeof(SharePointSources))]
+[JsonSerializable(typeof(SharePointSource))]
+[JsonSerializable(typeof(SlackSource))]
+[JsonSerializable(typeof(SlackSourceSlackChannels))]
+[JsonSerializable(typeof(SlackSourceSlackChannelsSlackChannel))]
+[JsonSerializable(typeof(UploadRagFileConfig))]
+[JsonSerializable(typeof(UploadRagFileRequest))]
+[JsonSerializable(typeof(UploadRagFileResponse))]
+[JsonSerializable(typeof(VertexAISearch))]
+[JsonSerializable(typeof(VertexAiSearchConfig))]
+[JsonSerializable(typeof(VertexRagStore))]
+[JsonSerializable(typeof(VertexRagStoreRagResource))]
+[JsonSerializable(typeof(SemanticRetrieverConfig))]
+[JsonSerializable(typeof(BatchCreateChunksRequest))]
+[JsonSerializable(typeof(BatchCreateChunksResponse))]
+[JsonSerializable(typeof(BatchDeleteChunksRequest))]
+[JsonSerializable(typeof(BatchUpdateChunksRequest))]
+[JsonSerializable(typeof(BatchUpdateChunksResponse))]
+[JsonSerializable(typeof(Chunk))]
+[JsonSerializable(typeof(ChunkData))]
+[JsonSerializable(typeof(CreateChunkRequest))]
+[JsonSerializable(typeof(List))]
+[JsonSerializable(typeof(DeleteChunkRequest))]
+[JsonSerializable(typeof(ListChunksResponse))]
+[JsonSerializable(typeof(UpdateChunkRequest))]
+[JsonSerializable(typeof(Condition))]
+[JsonSerializable(typeof(Corpus))]
+[JsonSerializable(typeof(ListCorporaResponse))]
+[JsonSerializable(typeof(MetadataFilter))]
+[JsonSerializable(typeof(QueryCorpusRequest))]
+[JsonSerializable(typeof(QueryCorpusResponse))]
+[JsonSerializable(typeof(RelevantChunk))]
+[JsonSerializable(typeof(CustomMetadata))]
+[JsonSerializable(typeof(Document))]
+[JsonSerializable(typeof(ListDocumentsResponse))]
+[JsonSerializable(typeof(QueryDocumentRequest))]
+[JsonSerializable(typeof(QueryDocumentResponse))]
+[JsonSerializable(typeof(StringList))]
+[JsonSerializable(typeof(ListPermissionsResponse))]
+[JsonSerializable(typeof(Permission))]
+[JsonSerializable(typeof(GenerateAnswerRequest))]
+[JsonSerializable(typeof(GenerateAnswerResponse))]
+[JsonSerializable(typeof(InputFeedback))]
+[JsonSerializable(typeof(Dataset))]
+[JsonSerializable(typeof(Hyperparameters))]
+[JsonSerializable(typeof(ListTunedModelsResponse))]
+[JsonSerializable(typeof(TunedModel))]
+[JsonSerializable(typeof(TunedModelSource))]
+[JsonSerializable(typeof(TuningExample))]
+[JsonSerializable(typeof(TuningExamples))]
+[JsonSerializable(typeof(TuningSnapshot))]
+[JsonSerializable(typeof(TuningTask))]
+[JsonSerializable(typeof(CredentialConfiguration))]
+[JsonSerializable(typeof(JsonNode))]
+[JsonSerializable(typeof(JsonElement))]
+[JsonSerializable(typeof(JsonObject))]
+[JsonSerializable(typeof(ClientSecrets))]
+
+[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, UseStringEnumConverter = true)]
+public partial class TypesSerializerContext : JsonSerializerContext
+{
+}
\ No newline at end of file
diff --git a/tests/AotTest/AotTest.csproj b/tests/AotTest/AotTest.csproj
new file mode 100644
index 00000000..4dfd7764
--- /dev/null
+++ b/tests/AotTest/AotTest.csproj
@@ -0,0 +1,24 @@
+
+
+
+ Exe
+ net9.0
+ enable
+ enable
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/AotTest/BookStoreService.cs b/tests/AotTest/BookStoreService.cs
new file mode 100644
index 00000000..73ae7ae2
--- /dev/null
+++ b/tests/AotTest/BookStoreService.cs
@@ -0,0 +1,38 @@
+using System.ComponentModel;
+using CSharpToJsonSchema;
+
+namespace AotTest;
+
+public class GetAuthorBook
+{
+ public string Title { get; set; } = string.Empty;
+ public string Description { get; set; } = string.Empty;
+}
+
+[GenerateJsonSchema]
+public interface IBookStoreService
+{
+ [Description("Get books written by some author")]
+ public Task> GetAuthorBooksAsync2([Description("Author name")] string authorName, CancellationToken cancellationToken = default);
+
+ [Description("Get book page content")]
+ public Task GetBookPageContentAsync2([Description("Book Name")] string bookName, [Description("Book Page Number")] int bookPageNumber, CancellationToken cancellationToken = default);
+
+}
+public class BookStoreService : IBookStoreService
+{
+ public Task> GetAuthorBooksAsync2(string authorName, CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult(new List([
+ 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" }
+ ]));
+ }
+
+ public Task GetBookPageContentAsync2(string bookName, int bookPageNumber, CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult("this is a cool weather out there, and I am stuck at home.");
+ }
+}
\ No newline at end of file
diff --git a/tests/AotTest/ComplexDataTypeService.cs b/tests/AotTest/ComplexDataTypeService.cs
new file mode 100644
index 00000000..aaf27722
--- /dev/null
+++ b/tests/AotTest/ComplexDataTypeService.cs
@@ -0,0 +1,77 @@
+using System.ComponentModel;
+using CSharpToJsonSchema;
+
+namespace AotTest;
+
+public class StudentRecord
+{
+ public enum GradeLevel
+ {
+ Freshman,
+ Sophomore,
+ Junior,
+ Senior,
+ Graduate
+ }
+
+ public string StudentId { get; set; } = string.Empty;
+ public string FullName { get; set; } = string.Empty;
+ public GradeLevel Level { get; set; } = GradeLevel.Freshman;
+ public List EnrolledCourses { get; set; } = new List();
+ public Dictionary Grades { get; set; } = new Dictionary();
+ public DateTime EnrollmentDate { get; set; } = DateTime.Now;
+ public bool IsActive { get; set; } = true;
+
+}
+
+[Description("Request class containing filters for querying student records.")]
+public class QueryStudentRecordRequest
+{
+ [Description("The student's full name.")]
+ public string FullName { get; set; } = string.Empty;
+
+ [Description("Grade filters for querying specific grades, e.g., Freshman or Senior.")]
+ public List GradeFilters { get; set; } = new();
+
+ [Description("The start date for the enrollment date range. ISO 8601 standard date")]
+ public DateTime EnrollmentStartDate { get; set; }
+
+ [Description("The end date for the enrollment date range. ISO 8601 standard date")]
+ public DateTime EnrollmentEndDate { get; set; }
+
+ [Description("The flag indicating whether to include only active students.")]
+ public bool? IsActive { get; set; } = true;
+}
+
+public class ComplexDataTypeService : IComplexDataTypeService
+{
+ [System.ComponentModel.Description("Get student record for the year")]
+ public async Task GetStudentRecordAsync(QueryStudentRecordRequest query,
+ CancellationToken cancellationToken = default)
+ {
+ return new StudentRecord
+ {
+ StudentId = "12345",
+ FullName = query.FullName,
+ Level = StudentRecord.GradeLevel.Senior,
+ EnrolledCourses = new List { "Math 101", "Physics 202", "History 303" },
+ Grades = new Dictionary
+ {
+ { "Math 101", 3.5 },
+ { "Physics 202", 3.8 },
+ { "History 303", 3.9 }
+ },
+ EnrollmentDate = new DateTime(2020, 9, 1),
+ IsActive = true
+ };
+ }
+}
+
+[GenerateJsonSchema()]
+public interface IComplexDataTypeService
+{
+ [Description("Get student record for the year")]
+ public Task GetStudentRecordAsync(QueryStudentRecordRequest query,
+ CancellationToken cancellationToken = default);
+}
+
diff --git a/tests/AotTest/JsonTests.cs b/tests/AotTest/JsonTests.cs
new file mode 100644
index 00000000..02f93d77
--- /dev/null
+++ b/tests/AotTest/JsonTests.cs
@@ -0,0 +1,256 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using GenerativeAI;
+using GenerativeAI.Core;
+using GenerativeAI.Types;
+using Shouldly;
+
+
+namespace AotTest;
+
+public class JsonModeTests
+{
+ private const string DefaultTestModelName = GoogleAIModels.DefaultGeminiModel;
+
+ private JsonSerializerOptions TestSerializerOptions
+ {
+ get
+ {
+ return new JsonSerializerOptions()
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
+ PropertyNameCaseInsensitive = true,
+ WriteIndented = true,
+ TypeInfoResolver = TestJsonSerializerContext.Default
+ };
+ }
+ }
+
+ ///
+ /// A helper method to create a GenerativeModel using the default Gemini model name,
+ /// matching the style used in basic tests.
+ ///
+ private GenerativeModel CreateInitializedModel()
+ {
+ var platform = GetTestGooglePlatform();
+
+ var model = new GenerativeModel(platform, DefaultTestModelName);
+ model.GenerateObjectJsonSerializerOptions = TestSerializerOptions;
+ return model;
+ }
+
+ #region GenerateObjectAsync Overloads
+
+ public async Task ShouldGenerateContentAsync_WithJsonMode_GenericParameter()
+ {
+ // Arrange
+ var model = CreateInitializedModel();
+
+ // We'll use a sample input request mimicking JSON-based generation
+ var request = new GenerateContentRequest();
+ request.AddText("Give me a really good message.", false);
+
+ // Act
+ var response = await model.GenerateContentAsync(request).ConfigureAwait(false);
+
+ // Assert
+ response.ShouldNotBeNull();
+ response.Text().ShouldNotBeNull();
+ var obj = response.ToObject(TestSerializerOptions);
+ obj.Message.ShouldNotBeNullOrWhiteSpace();
+ // Additional checks as needed for any placeholders or content
+ Console.WriteLine("GenerateContentAsync returned a valid GenerateContentResponse.");
+ }
+
+
+ public async Task ShouldGenerateObjectAsync_WithGenericParameter()
+ {
+ // Arrange
+ var model = CreateInitializedModel();
+
+ var request = new GenerateContentRequest();
+ request.AddText("write a text message for my boss that I'm resigning from the job.", false);
+
+ // Act
+ var result = await model.GenerateObjectAsync(request).ConfigureAwait(false);
+
+ // Assert
+ result.ShouldNotBeNull();
+ result.Message.ShouldNotBeNullOrWhiteSpace();
+ Console.WriteLine($"GenerateObjectAsync(request) returned: {result.Message}");
+ }
+
+
+ public async Task ShouldGenerateObjectAsync_WithStringPrompt()
+ {
+ // Arrange
+ var model = CreateInitializedModel();
+ var prompt = "I need a birthday message for my wife.";
+
+ // Act
+ var result = await model.GenerateObjectAsync(prompt).ConfigureAwait(false);
+
+ // Assert
+ result.ShouldNotBeNull();
+ result.Message.ShouldNotBeNullOrWhiteSpace();
+ Console.WriteLine($"GenerateObjectAsync(string prompt) returned: {result.Message}");
+ }
+
+
+ public async Task ShouldGenerateObjectAsync_WithPartsEnumerable()
+ {
+ // Arrange
+ var model = CreateInitializedModel();
+
+ // Build content parts with an imaginary scenario
+ var parts = new List
+ {
+ new Part() { Text = "I am very busy person. i always need AI help for my work." },
+ new Part() { Text = "I need a message for my boss to provide me a paid subscription to Gemini Advanced." }
+ };
+
+ // Act
+ var result = await model.GenerateObjectAsync(parts).ConfigureAwait(false);
+
+ // Assert
+ result.ShouldNotBeNull();
+ result.Message.ShouldNotBeNullOrWhiteSpace();
+ Console.WriteLine($"GenerateObjectAsync(IEnumerable parts) returned: {result.Message}");
+ }
+
+ #endregion
+
+
+ public async Task ShouldGenerateComplexObjectAsync_WithVariousDataTypes()
+ {
+ // Arrange
+ var model = CreateInitializedModel();
+ var request = new GenerateContentRequest();
+ request.AddText(
+ "Generate a structured object with various data types including dictionary, list, array, and nested objects.",
+ false);
+
+ // Act
+ var response = await model.GenerateContentAsync(request).ConfigureAwait(false);
+
+ // Assert
+ response.ShouldNotBeNull();
+ response.Text().ShouldNotBeNullOrWhiteSpace();
+ var obj = response.ToObject(TestSerializerOptions);
+
+ obj.Title.ShouldNotBeNullOrWhiteSpace();
+ // obj.Metadata.ShouldNotBeNull();
+ // obj.Metadata.ShouldContainKey("key1");
+ obj.Numbers.ShouldNotBeNull();
+ obj.Numbers.Length.ShouldBeGreaterThan(0);
+ obj.Children.ShouldNotBeNull();
+ obj.Children.ForEach(child =>
+ {
+ child.Name.ShouldNotBeNullOrWhiteSpace();
+ child.Values.ShouldNotBeNull();
+ child.Values.ShouldNotBeEmpty();
+ });
+
+ //obj.OptionalField.ShouldBeNull();
+ Console.WriteLine("GenerateContentAsync with various data types returned a valid response.");
+ }
+
+
+ public async Task ShouldGenerateNestedObjectAsync_WithJsonMode()
+ {
+ // Arrange
+ var model = CreateInitializedModel();
+ var request = new GenerateContentRequest();
+ request.AddText("Generate a complex JSON object with nested properties.", false);
+
+ // Act
+ var response = await model.GenerateContentAsync(request).ConfigureAwait(false);
+
+ // Assert
+ response.ShouldNotBeNull();
+
+ response.Text().ShouldNotBeNullOrWhiteSpace();
+ var obj = response.ToObject(TestSerializerOptions);
+ obj.Description.ShouldNotBeNullOrWhiteSpace();
+ obj.Details.ShouldNotBeNull();
+ obj.Details.Title.ShouldNotBeNullOrWhiteSpace();
+ obj.Children.ShouldNotBeNull();
+ obj.Children.ShouldNotBeEmpty();
+ obj.Children.ForEach(child =>
+ {
+ child.Name.ShouldNotBeNullOrWhiteSpace();
+ child.Values.ShouldNotBeNull();
+ child.Values.ShouldNotBeEmpty();
+ });
+ Console.WriteLine("GenerateContentAsync with nested types returned a valid response.");
+ }
+
+
+ protected virtual IPlatformAdapter GetTestGooglePlatform()
+ {
+ //return GetTestVertexAIPlatform();
+ var apiKey = Environment.GetEnvironmentVariable("GEMINI_API_KEY", EnvironmentVariableTarget.User);
+
+ return new GoogleAIPlatformAdapter(apiKey);
+ }
+}
+
+///
+/// A complex sample class with nested classes and collections used for testing JSON deserialization.
+///
+internal class ComplexJsonClass
+{
+ public string? Description { get; set; }
+ public Detail? Details { get; set; }
+ public List? Children { get; set; }
+
+ public class Detail
+ {
+ public string? Title { get; set; }
+ public string? Content { get; set; }
+ }
+
+ public class Child2
+ {
+ public string? Name { get; set; }
+ public List? Values { get; set; }
+ }
+}
+
+///
+/// A small sample class used for testing JSON deserialization.
+/// The property name can be adjusted as needed for your test scenarios.
+///
+internal class SampleJsonClass
+{
+ public string? Message { get; set; }
+}
+
+///
+/// A sample class used to test serialization and deserialization with various data types.
+///
+internal class ComplexDataTypeClass
+{
+ public string? Title { get; set; }
+ [JsonIgnore] public Dictionary? Metadata { get; set; }
+ public int[]? Numbers { get; set; }
+ public List? Children { get; set; }
+ public string? OptionalField { get; set; }
+
+ public class Child
+ {
+ public string? Name { get; set; }
+ public List? Values { get; set; }
+ }
+}
+
+[JsonSerializable(typeof(SampleJsonClass))]
+[JsonSerializable(typeof(ComplexDataTypeClass.Child))]
+[JsonSerializable(typeof(ComplexJsonClass.Child2))]
+[JsonSerializable(typeof(ComplexJsonClass.Detail))]
+[JsonSerializable(typeof(ComplexDataTypeClass))]
+[JsonSerializable(typeof(ComplexJsonClass))]
+[JsonSourceGenerationOptions(WriteIndented = true)]
+internal partial class TestJsonSerializerContext : JsonSerializerContext
+{
+}
\ No newline at end of file
diff --git a/tests/AotTest/LiveTest.cs b/tests/AotTest/LiveTest.cs
new file mode 100644
index 00000000..40f7d8dc
--- /dev/null
+++ b/tests/AotTest/LiveTest.cs
@@ -0,0 +1,53 @@
+using GenerativeAI;
+using GenerativeAI.Live;
+using GenerativeAI.Types;
+using Microsoft.Extensions.Logging;
+
+namespace AotTest;
+
+public class LiveTest
+{
+ public async Task ShouldRunMultiModalLive()
+ {
+ var exitEvent = new ManualResetEvent(false);
+ var multiModalLive = new MultiModalLiveClient(new GoogleAIPlatformAdapter(EnvironmentVariables.GOOGLE_API_KEY),
+ "gemini-2.0-flash-exp", new GenerationConfig()
+ {
+ ResponseModalities = [Modality.TEXT]
+ });
+ multiModalLive.MessageReceived += (sender, e) =>
+ {
+ if (e.Payload.SetupComplete != null)
+ {
+ System.Console.WriteLine($"Setup complete: {e.Payload.SetupComplete}");
+ }
+
+ Console.WriteLine("Payload received.");
+ if (e.Payload.ServerContent != null)
+ {
+ if (e.Payload.ServerContent.ModelTurn != null)
+ {
+ foreach (var s in e.Payload.ServerContent.ModelTurn?.Parts.Select(s => s.Text))
+ {
+ System.Console.Write(s);
+ }
+
+ if (e.Payload.ServerContent.TurnComplete == true)
+ {
+ System.Console.WriteLine();
+ }
+ }
+ }
+ };
+ multiModalLive.UseGoogleSearch = true;
+ await multiModalLive.ConnectAsync();
+ var content = "write a poem about stars";
+ var clientContent = new BidiGenerateContentClientContent();
+ clientContent.Turns = new[] { new Content(content, Roles.User) };
+ clientContent.TurnComplete = true;
+ await multiModalLive.SendClientContentAsync(clientContent);
+
+ Task.WaitAll();
+ await multiModalLive.DisconnectAsync();
+ }
+}
\ No newline at end of file
diff --git a/tests/AotTest/MEAITests.cs b/tests/AotTest/MEAITests.cs
new file mode 100644
index 00000000..4953cd4d
--- /dev/null
+++ b/tests/AotTest/MEAITests.cs
@@ -0,0 +1,80 @@
+using CSharpToJsonSchema;
+using GenerativeAI.Microsoft;
+using Microsoft.Extensions.AI;
+using Shouldly;
+
+
+namespace AotTest;
+
+public class MEAITests
+{
+
+ public async Task ShouldWorkWithTools()
+ {
+
+ var apiKey = Environment.GetEnvironmentVariable("GEMINI_API_KEY", EnvironmentVariableTarget.User);
+ var chatClient = new GenerativeAIChatClient(apiKey);
+ var chatOptions = new ChatOptions();
+
+ var tools = new Tools([GetCurrentWeather]);
+ chatOptions.Tools = tools.AsMeaiTools();
+ var message = new ChatMessage(ChatRole.User, "What is the weather in New York in celsius?");
+ var response = await chatClient.GetResponseAsync(message,options:chatOptions).ConfigureAwait(false);
+
+ Console.WriteLine(response.Choices.LastOrDefault().Text);
+ response.Choices.LastOrDefault().Text.Contains("New York", StringComparison.InvariantCultureIgnoreCase);
+ }
+
+
+ public async Task ShouldWorkWith_BookStoreService()
+ {
+
+ var apiKey = Environment.GetEnvironmentVariable("GEMINI_API_KEY", EnvironmentVariableTarget.User);
+ var chatClient = new GenerativeAIChatClient(apiKey);
+ var chatOptions = new ChatOptions();
+
+
+ var tools = new Tools([GetBookPageContentAsync]);
+ chatOptions.Tools = tools.AsMeaiTools();
+
+ var message = new ChatMessage(ChatRole.User, "what is written on page 96 in the book 'damdamadum'");
+ var response = await chatClient.GetResponseAsync(message,options:chatOptions).ConfigureAwait(false);
+
+ response.Choices.LastOrDefault().Text.ShouldContain("damdamadum",Case.Insensitive);
+ }
+
+ [FunctionTool(MeaiFunctionTool = true)]
+ [System.ComponentModel.Description("Get book page content")]
+ public static Task GetBookPageContentAsync(string bookName, int bookPageNumber, CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult("this is a cool weather out there, and I am stuck at home.");
+ }
+
+ [FunctionTool(MeaiFunctionTool = true)]
+ [System.ComponentModel.Description("Get the current weather in a given location")]
+ public Weather GetCurrentWeather(string location, Unit unit = Unit.Celsius)
+ {
+ return new Weather
+ {
+ Location = location,
+ Temperature = 30.0,
+ Unit = unit,
+ Description = "Sunny",
+ };
+ }
+
+ public enum Unit
+ {
+ Celsius,
+ Fahrenheit,
+ Imperial
+ }
+
+ public class Weather
+ {
+ public string Location { get; set; } = string.Empty;
+ public double Temperature { get; set; }
+ public Unit Unit { get; set; }
+ public string Description { get; set; } = string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/tests/AotTest/Program.cs b/tests/AotTest/Program.cs
new file mode 100644
index 00000000..3cdbfc2d
--- /dev/null
+++ b/tests/AotTest/Program.cs
@@ -0,0 +1,26 @@
+// See https://aka.ms/new-console-template for more information
+
+using AotTest;
+
+
+var testClass = new JsonModeTests();
+
+await testClass.ShouldGenerateComplexObjectAsync_WithVariousDataTypes();
+ await testClass.ShouldGenerateObjectAsync_WithGenericParameter();
+ await testClass.ShouldGenerateObjectAsync_WithPartsEnumerable();
+ await testClass.ShouldGenerateContentAsync_WithJsonMode_GenericParameter();
+ await testClass.ShouldGenerateNestedObjectAsync_WithJsonMode();
+ await testClass.ShouldGenerateObjectAsync_WithStringPrompt();
+
+ var liveTest = new LiveTest();
+ await liveTest.ShouldRunMultiModalLive();
+
+var toolsTest = new WeatherServiceTests();
+await toolsTest.ShouldInvokeWetherService();
+await toolsTest.ShouldWorkWith_BookStoreService();
+await toolsTest.ShouldWorkWith_ComplexDataTypes();
+
+var meai = new MEAITests();
+await meai.ShouldWorkWith_BookStoreService();
+await meai.ShouldWorkWithTools();
+
diff --git a/tests/AotTest/WeatherService.cs b/tests/AotTest/WeatherService.cs
new file mode 100644
index 00000000..02fec9e1
--- /dev/null
+++ b/tests/AotTest/WeatherService.cs
@@ -0,0 +1,65 @@
+using CSharpToJsonSchema;
+using DescriptionAttribute = System.ComponentModel.DescriptionAttribute;
+
+namespace AotTest
+{
+ public enum Unit
+ {
+ Celsius,
+ Fahrenheit,
+ Imperial
+ }
+
+ public class Weather
+ {
+ public string Location { get; set; } = string.Empty;
+ public double Temperature { get; set; }
+ public Unit Unit { get; set; }
+ public string Description { get; set; } = string.Empty;
+ }
+
+ [GenerateJsonSchema()]
+ public interface IWeatherFunctions
+ {
+ [Description("Get the current weather in a given location")]
+ public Weather GetCurrentWeather2(
+ [Description("The city and state, e.g. San Francisco, CA")]
+ string location,
+ Unit unit = Unit.Celsius);
+
+ [Description("Get the current weather in a given location")]
+ public Task GetCurrentWeatherAsync2(
+ [Description("The city and state, e.g. San Francisco, CA")]
+ string location,
+ Unit unit = Unit.Celsius,
+ CancellationToken cancellationToken = default);
+ }
+
+ [Description("Weather Functions")]
+ public class WeatherService : IWeatherFunctions
+ {
+ [Description("Get the current weather in a given location")]
+ public Weather GetCurrentWeather2(string location, Unit unit = Unit.Celsius)
+ {
+ return new Weather
+ {
+ Location = location,
+ Temperature = 30.0,
+ Unit = unit,
+ Description = "Sunny",
+ };
+ }
+
+ public Task GetCurrentWeatherAsync2(string location, Unit unit = Unit.Celsius,
+ CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult(new Weather
+ {
+ Location = location,
+ Temperature = 22.0,
+ Unit = unit,
+ Description = "Sunny",
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/AotTest/WeatherServiceTests.cs b/tests/AotTest/WeatherServiceTests.cs
new file mode 100644
index 00000000..46bb8631
--- /dev/null
+++ b/tests/AotTest/WeatherServiceTests.cs
@@ -0,0 +1,54 @@
+using GenerativeAI;
+using GenerativeAI.Core;
+using GenerativeAI.Tools;
+
+namespace AotTest;
+
+public class WeatherServiceTests
+{
+
+ public async Task ShouldInvokeWetherService()
+ {
+ WeatherService service = new WeatherService();
+ var tools = service.AsTools();
+ var calls = service.AsCalls();
+ var tool = new GenericFunctionTool(tools, calls);
+
+ var model = new GenerativeModel(GetTestGooglePlatform(), GoogleAIModels.DefaultGeminiModel);
+
+ model.AddFunctionTool(tool);
+
+ var result = await model.GenerateContentAsync("What is the weather in san francisco today?").ConfigureAwait(false);
+
+ Console.WriteLine(result.Text());
+ }
+
+
+ public async Task ShouldWorkWith_BookStoreService()
+ {
+ var service = new BookStoreService();
+ var tool = new GenericFunctionTool(service.AsTools(), service.AsCalls());
+ var model = new GenerativeModel(GetTestGooglePlatform(), GoogleAIModels.DefaultGeminiModel);
+ model.AddFunctionTool(tool);
+ var result = await model.GenerateContentAsync("what is written on page 35 in the book 'abracadabra'").ConfigureAwait(false);
+ Console.WriteLine(result.Text());
+ }
+
+ public async Task ShouldWorkWith_ComplexDataTypes()
+ {
+ var service = new ComplexDataTypeService();
+ var tool = new GenericFunctionTool(service.AsTools(), service.AsCalls());
+ var model = new GenerativeModel(GetTestGooglePlatform(), GoogleAIModels.Gemini2Flash);
+ model.AddFunctionTool(tool);
+ var result = await model.GenerateContentAsync("how's Deepak Siwach is doing in Senior Grade for enrollment year 01-01-2024 to 01-01-2025").ConfigureAwait(false);
+ Console.WriteLine(result.Text());
+ }
+
+ protected virtual IPlatformAdapter GetTestGooglePlatform()
+ {
+ //return GetTestVertexAIPlatform();
+ var apiKey = Environment.GetEnvironmentVariable("GEMINI_API_KEY", EnvironmentVariableTarget.User);
+
+ return new GoogleAIPlatformAdapter(apiKey);
+ }
+}
\ No newline at end of file
diff --git a/tests/GenerativeAI.Auth.Tests/ServiceAccount_Tests.cs b/tests/GenerativeAI.Auth.Tests/ServiceAccount_Tests.cs
index c057bfb1..2197da2b 100644
--- a/tests/GenerativeAI.Auth.Tests/ServiceAccount_Tests.cs
+++ b/tests/GenerativeAI.Auth.Tests/ServiceAccount_Tests.cs
@@ -1,7 +1,6 @@
using GenerativeAI.Authenticators;
using GenerativeAI.Core;
using GenerativeAI.Tests;
-using Humanizer;
using Shouldly;
namespace GenerativeAI.Auth;
diff --git a/tests/GenerativeAI.IntegrationTests/GenerativeAI.IntegrationTests.csproj b/tests/GenerativeAI.IntegrationTests/GenerativeAI.IntegrationTests.csproj
index 9a3caf7e..c40cd5ba 100644
--- a/tests/GenerativeAI.IntegrationTests/GenerativeAI.IntegrationTests.csproj
+++ b/tests/GenerativeAI.IntegrationTests/GenerativeAI.IntegrationTests.csproj
@@ -1,17 +1,18 @@
- net9.0
+ net6.0;net9.0
enable
enable
latest
false
true
- Exe
+ Exe
+
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/tests/GenerativeAI.IntegrationTests/QuickTool_Tests.cs b/tests/GenerativeAI.IntegrationTests/QuickTool_Tests.cs
new file mode 100644
index 00000000..6ef61e60
--- /dev/null
+++ b/tests/GenerativeAI.IntegrationTests/QuickTool_Tests.cs
@@ -0,0 +1,241 @@
+using System.ComponentModel;
+using System.Text.Json.Nodes;
+using GenerativeAI.Tests;
+using GenerativeAI.Tools;
+using GenerativeAI.Types;
+using Shouldly;
+
+namespace GenerativeAI.IntegrationTests;
+
+public class QuickTool_Tests : TestBase
+{
+ public QuickTool_Tests(ITestOutputHelper helper) : base(helper)
+ {
+ }
+
+ [Fact]
+ public async Task ShouldCreateQuickTool_Async()
+ {
+ var func =
+ (async ([Description("Student Name")] string studentName,
+ [Description("Student Grade")] GradeLevel grade) =>
+ {
+ return
+ $"{studentName} in {grade} grade is achieving remarkable scores in math and physics, showcasing outstanding progress.";
+ });
+
+ var quickFt = new QuickTool(func, "GetStudentRecordAsync", "Return student record for the year");
+
+ var args = new JsonObject();
+ args.Add("studentName", "John");
+ args.Add("grade", "Freshman");
+ var res = await quickFt.CallAsync(new FunctionCall()
+ {
+ Name = "GetStudentRecordAsync",
+ Args = args
+ });
+
+ (res.Response as JsonNode)["content"].GetValue().ShouldContain("John");
+ }
+
+ [Fact]
+ public async Task ShouldCreateQuickTool()
+ {
+ var func =
+ (([Description("Student Name")] string studentName, [Description("Student Grade")] GradeLevel grade) =>
+ {
+ return
+ $"{studentName} in {grade} grade is achieving remarkable scores in math and physics, showcasing outstanding progress.";
+ });
+
+ var quickFt = new QuickTool(func, "GetStudentRecordAsync", "Return student record for the year");
+
+ var args = new JsonObject();
+ args.Add("studentName", "John");
+ args.Add("grade", "Freshman");
+ var res = await quickFt.CallAsync(new FunctionCall()
+ {
+ Name = "GetStudentRecordAsync",
+ Args = args
+ });
+ (res.Response as JsonNode)["content"].GetValue().ShouldContain("John");
+ }
+
+ [Fact]
+ public async Task ShouldCreateQuickTool_void()
+ {
+ bool invoked = false;
+ var func =
+ (([Description("Student Name")] string studentName, [Description("Student Grade")] GradeLevel grade) =>
+ {
+ var str =
+ $"{studentName} in {grade} grade is achieving remarkable scores in math and physics, showcasing outstanding progress.";
+ Console.WriteLine(str);
+ invoked = true;
+ });
+
+ var quickFt = new QuickTool(func, "GetStudentRecordAsync", "Return student record for the year");
+
+ var args = new JsonObject();
+ args.Add("studentName", "John");
+ args.Add("grade", "Freshman");
+ var res = await quickFt.CallAsync(new FunctionCall()
+ {
+ Name = "GetStudentRecordAsync",
+ Args = args
+ });
+ invoked.ShouldBeTrue();
+ (res.Response as JsonNode)["content"].GetValue().ShouldBeEmpty();
+ }
+
+ [Fact]
+ public async Task ShouldCreateQuickTool_Task()
+ {
+ bool invoked = false;
+ var func = (async ([Description("Student Name")] string studentName,
+ [Description("Student Grade")] GradeLevel grade) =>
+ {
+ var str =
+ $"{studentName} in {grade} grade is achieving remarkable scores in math and physics, showcasing outstanding progress.";
+ await Task.Delay(100);
+ invoked = true;
+ });
+
+ var quickFt = new QuickTool(func, "GetStudentRecordAsync", "Return student record for the year");
+
+ var args = new JsonObject();
+ args.Add("studentName", "John");
+ args.Add("grade", "Freshman");
+ var res = await quickFt.CallAsync(new FunctionCall()
+ {
+ Name = "GetStudentRecordAsync",
+ Args = args
+ });
+ invoked.ShouldBeTrue();
+ (res.Response as JsonNode)["content"].GetValue().ShouldBeEmpty();
+
+ quickFt.FunctionDeclaration.Parameters.ShouldSatisfyAllConditions(
+ parameters =>
+ {
+ parameters.ShouldNotBeNull();
+ parameters.Properties.Keys.ShouldContain("studentName");
+ parameters.Properties.Keys.ShouldContain("grade");
+ parameters.Properties["studentName"].Type.ShouldBe("string");
+ parameters.Properties["studentName"].Description.ShouldBe("Student Name");
+ parameters.Properties["grade"].Type.ShouldBe("string");
+ parameters.Properties["grade"].Description.ShouldBe("Student Grade");
+
+ });
+ }
+
+ [Fact]
+ public async Task ShouldCreateQuickTool_ComplexDataTypes()
+ {
+ var func = (async ([Description("Request to query student record")] QueryStudentRecordRequest query) =>
+ {
+ return new StudentRecord
+ {
+ StudentId = "12345",
+ FullName = "John Doe",
+ Level = GradeLevel.Freshman,
+ EnrolledCourses = new List { "Math", "Physics", "Chemistry" },
+ Grades = new Dictionary
+ {
+ { "Math", 95.0 },
+ { "Physics", 89.0 },
+ { "Chemistry", 88.0 }
+ },
+ EnrollmentDate = new DateTime(2023, 1, 10),
+ IsActive = true
+ };
+ });
+
+
+
+ var quickFt = new QuickTool(func, "GetStudentRecordAsync", "Return student record for the year");
+
+ var args = new JsonObject();
+ args.Add("studentName", "John");
+ args.Add("grade", "Freshman");
+ var res = await quickFt.CallAsync(new FunctionCall()
+ {
+ Name = "GetStudentRecordAsync",
+ Args = args
+ });
+
+ var content = res.Response as JsonNode;
+ content = content["content"] as JsonObject;
+ content["studentId"].GetValue().ShouldBe("12345");
+ content["fullName"].GetValue().ShouldBe("John Doe");
+ content["level"].GetValue().ShouldBe("Freshman");
+ content["enrolledCourses"].AsArray().Select(x => x.GetValue())
+ .ShouldBe(new List { "Math", "Physics", "Chemistry" });
+ content["grades"]["Math"].GetValue().ShouldBe(95.0);
+ content["grades"]["Physics"].GetValue().ShouldBe(89.0);
+ content["grades"]["Chemistry"].GetValue().ShouldBe(88.0);
+ content["enrollmentDate"].GetValue().ShouldBe(new DateTime(2023, 1, 10));
+ content["isActive"].GetValue().ShouldBe(true);
+
+ quickFt.FunctionDeclaration.Parameters.ShouldSatisfyAllConditions(
+ schema =>
+ {
+ schema.ShouldNotBeNull();
+
+ var querySchema = schema.Properties["query"];
+ querySchema.Properties.Keys.ShouldBe(new[]
+ {
+ "fullName", "gradeFilters", "enrollmentStartDate", "enrollmentEndDate", "isActive"
+ });
+ querySchema.Properties["fullName"].Type.ShouldBe("string");
+ querySchema.Properties["fullName"].Description.ShouldBe("The student's full name.");
+ querySchema.Properties["gradeFilters"].Type.ShouldBe("array");
+ querySchema.Properties["gradeFilters"].Description.ShouldBe("Grade filters for querying specific grades, e.g., Freshman or Senior.");
+ querySchema.Properties["enrollmentStartDate"].Type.ShouldBe("string");
+ querySchema.Properties["enrollmentStartDate"].Format.ShouldBe("date-time");
+ querySchema.Properties["enrollmentStartDate"].Description.ShouldBe("The start date for the enrollment date range. ISO 8601 standard date");
+ querySchema.Properties["enrollmentEndDate"].Type.ShouldBe("string");
+ querySchema.Properties["enrollmentEndDate"].Format.ShouldBe("date-time");
+ querySchema.Properties["enrollmentEndDate"].Description.ShouldBe("The end date for the enrollment date range. ISO 8601 standard date");
+ querySchema.Properties["isActive"].Type.ShouldBe("boolean");
+ querySchema.Properties["isActive"].Description.ShouldBe("The flag indicating whether to include only active students.");
+ });
+ }
+
+ [Fact]
+ public async Task ShouldInvokeWetherService()
+ {
+ Assert.SkipUnless(IsGeminiApiKeySet,GeminiTestSkipMessage);
+
+ var func = (async ([Description("Request to query student record")] QueryStudentRecordRequest query) =>
+ {
+ return new StudentRecord
+ {
+ StudentId = "12345",
+ FullName = query.FullName,
+ Level = GradeLevel.Freshman,
+ EnrolledCourses = new List { "Math", "Physics", "Chemistry" },
+ Grades = new Dictionary
+ {
+ { "Math", 95.0 },
+ { "Physics", 89.0 },
+ { "Chemistry", 88.0 }
+ },
+ EnrollmentDate = new DateTime(2023, 1, 10),
+ IsActive = true
+ };
+ });
+
+ var quickFt = new QuickTool(func, "GetStudentRecordAsync", "Return student record for the year");
+
+ var tool = quickFt;
+
+ var model = new GenerativeModel(GetTestGooglePlatform(), GoogleAIModels.Gemini2Flash);
+
+ model.AddFunctionTool(tool);
+
+ var result = await model.GenerateContentAsync("How's Amit Rana is doing in Senior Grade? in enrollment year 01-01-2024 to 01-01-2025").ConfigureAwait(false);
+
+ result.Text().ShouldContain("Amit Rana",Case.Insensitive);
+ Console.WriteLine(result.Text());
+ }
+}
\ No newline at end of file
diff --git a/tests/GenerativeAI.IntegrationTests/BookStoreService.cs b/tests/GenerativeAI.IntegrationTests/Services/BookStoreService.cs
similarity index 100%
rename from tests/GenerativeAI.IntegrationTests/BookStoreService.cs
rename to tests/GenerativeAI.IntegrationTests/Services/BookStoreService.cs
diff --git a/tests/GenerativeAI.IntegrationTests/Services/StudentRecord_ComplexDataTypes.cs b/tests/GenerativeAI.IntegrationTests/Services/StudentRecord_ComplexDataTypes.cs
new file mode 100644
index 00000000..c4d62344
--- /dev/null
+++ b/tests/GenerativeAI.IntegrationTests/Services/StudentRecord_ComplexDataTypes.cs
@@ -0,0 +1,49 @@
+using System.ComponentModel;
+
+namespace GenerativeAI.IntegrationTests;
+
+public enum GradeLevel
+{
+ Freshman,
+ Sophomore,
+ Junior,
+ Senior,
+ Graduate
+}
+
+public class StudentRecord
+{
+
+ public string StudentId { get; set; } = string.Empty;
+ public string FullName { get; set; } = string.Empty;
+ public GradeLevel Level { get; set; } = GradeLevel.Freshman;
+ public List EnrolledCourses { get; set; } = new List();
+ public Dictionary Grades { get; set; } = new Dictionary();
+ public DateTime EnrollmentDate { get; set; } = DateTime.Now;
+ public bool IsActive { get; set; } = true;
+
+ public double CalculateGPA()
+ {
+ if (Grades.Count == 0) return 0.0;
+ return Grades.Values.Average();
+ }
+}
+
+[Description("Request class containing filters for querying student records.")]
+public class QueryStudentRecordRequest
+{
+ [Description("The student's full name.")]
+ public string FullName { get; set; } = string.Empty;
+
+ [Description("Grade filters for querying specific grades, e.g., Freshman or Senior.")]
+ public List GradeFilters { get; set; } = new();
+
+ [Description("The start date for the enrollment date range. ISO 8601 standard date")]
+ public DateTime EnrollmentStartDate { get; set; }
+
+ [Description("The end date for the enrollment date range. ISO 8601 standard date")]
+ public DateTime EnrollmentEndDate { get; set; }
+
+ [Description("The flag indicating whether to include only active students.")]
+ public bool? IsActive { get; set; } = true;
+}
\ No newline at end of file
diff --git a/tests/GenerativeAI.IntegrationTests/WeatherService.cs b/tests/GenerativeAI.IntegrationTests/Services/WeatherService.cs
similarity index 100%
rename from tests/GenerativeAI.IntegrationTests/WeatherService.cs
rename to tests/GenerativeAI.IntegrationTests/Services/WeatherService.cs
diff --git a/tests/GenerativeAI.IntegrationTests/WeatherServiceTests.cs b/tests/GenerativeAI.IntegrationTests/WeatherServiceTests.cs
index 125ef391..50eae5fd 100644
--- a/tests/GenerativeAI.IntegrationTests/WeatherServiceTests.cs
+++ b/tests/GenerativeAI.IntegrationTests/WeatherServiceTests.cs
@@ -21,7 +21,6 @@ public async Task ShouldInvokeWetherService()
var model = new GenerativeModel(GetTestGooglePlatform(), GoogleAIModels.DefaultGeminiModel);
model.AddFunctionTool(tool);
-
var result = await model.GenerateContentAsync("What is the weather in san francisco today?").ConfigureAwait(false);
diff --git a/tests/GenerativeAI.Live.Tests/GenerativeAI.Live.Tests.csproj b/tests/GenerativeAI.Live.Tests/GenerativeAI.Live.Tests.csproj
index f46d287b..13a487dd 100644
--- a/tests/GenerativeAI.Live.Tests/GenerativeAI.Live.Tests.csproj
+++ b/tests/GenerativeAI.Live.Tests/GenerativeAI.Live.Tests.csproj
@@ -6,12 +6,14 @@
enable
false
Exe
+ true
-
-
+
+
+
diff --git a/tests/GenerativeAI.Live.Tests/MultiModalLive.cs b/tests/GenerativeAI.Live.Tests/MultiModalLive.cs
index 0e7cd7da..2c0327a1 100644
--- a/tests/GenerativeAI.Live.Tests/MultiModalLive.cs
+++ b/tests/GenerativeAI.Live.Tests/MultiModalLive.cs
@@ -1,9 +1,11 @@
using GenerativeAI.Clients;
using GenerativeAI.Live;
using GenerativeAI.Types;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Console;
+using Microsoft.Extensions.Options;
namespace GenerativeAI.Tests.Model;
-
public class MultiModalLive_Tests
{
public static async Task Main(string[] args)
@@ -13,9 +15,17 @@ public static async Task Main(string[] args)
public async Task ShouldRunMultiModalLive()
{
+ var logger = LoggerFactory.Create((s) =>
+ {
+ s.AddSimpleConsole();
+ }).CreateLogger();
+
var exitEvent = new ManualResetEvent(false);
var multiModalLive = new MultiModalLiveClient(new GoogleAIPlatformAdapter(EnvironmentVariables.GOOGLE_API_KEY),
- "gemini-2.0-flash-exp");
+ "gemini-2.0-flash-exp", new GenerationConfig()
+ {
+ ResponseModalities = [Modality.TEXT]
+ },logger:logger);
multiModalLive.MessageReceived += (sender, e) =>
{
if (e.Payload.SetupComplete != null)
@@ -23,6 +33,7 @@ public async Task ShouldRunMultiModalLive()
System.Console.WriteLine($"Setup complete: {e.Payload.SetupComplete}");
}
+ Console.WriteLine("Payload received.");
if (e.Payload.ServerContent != null)
{
if (e.Payload.ServerContent.ModelTurn != null)
diff --git a/tests/GenerativeAI.Microsoft.Tests/MicrosoftExtension_Tests.cs b/tests/GenerativeAI.Microsoft.Tests/MicrosoftExtension_Tests.cs
index 2506d4da..9f46e803 100644
--- a/tests/GenerativeAI.Microsoft.Tests/MicrosoftExtension_Tests.cs
+++ b/tests/GenerativeAI.Microsoft.Tests/MicrosoftExtension_Tests.cs
@@ -1,4 +1,5 @@
using System.Text.Json;
+using System.Text.Json.Nodes;
using Bogus;
using GenerativeAI.IntegrationTests;
using GenerativeAI.Microsoft.Extensions;
@@ -191,7 +192,7 @@ public void ToAiContents_WithTextPart_ReturnsTextContent()
public void ToAiContents_WithFunctionCallPart_ReturnsFunctionCallContent()
{
// Arrange
- var parts = new List { new Part { FunctionCall = new FunctionCall { Name = "myFunction", Args = new { arg1 = "value1", arg2 = "value2" } } } };
+ var parts = new List { new Part { FunctionCall = new FunctionCall { Name = "myFunction", Args = null } } };
// Act
var result = parts.ToAiContents();
@@ -210,7 +211,7 @@ public void ToAiContents_WithFunctionCallPart_ReturnsFunctionCallContent()
public void ToAiContents_WithFunctionResponsePart_ReturnsFunctionResultContent()
{
// Arrange
- var parts = new List { new Part { FunctionResponse = new FunctionResponse { Name = "myFunction", Response = new { result = "success" } } } };
+ var parts = new List { new Part { FunctionResponse = new FunctionResponse { Name = "myFunction", Response = JsonNode.Parse("{ \"result\": \"value\" }") } } };
// Act
var result = parts.ToAiContents();
@@ -250,7 +251,7 @@ public void ToAiContents_WithMultipleParts_ReturnsMultipleContents()
var parts = new List
{
new Part { Text = "Hello, world!" },
- new Part { FunctionCall = new FunctionCall { Name = "myFunction", Args = new { arg1 = "value1" } } },
+ new Part { FunctionCall = new FunctionCall { Name = "myFunction", Args = null } },
new Part { InlineData = new Blob { MimeType = "image/png", Data = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M/wHwAFAAH/8mdr1QAAAABJRU5ErkJggg==" } }
};
@@ -279,14 +280,14 @@ public void ToAiContents_BogusData_HandlesVariousParts()
else
{
o.FunctionCall = f.Random.Bool(0.5f)
- ? new FunctionCall { Name = f.Internet.DomainName(), Args = new Faker().Generate()}
+ ? new FunctionCall { Name = f.Internet.DomainName(), Args = null }
: null;
}
if (f.Random.Bool(0.33f))
{
o.FunctionResponse = f.Random.Bool(0.5f)
- ? new FunctionResponse { Name = f.Internet.DomainName(), Response = new Faker().Generate() }
+ ? new FunctionResponse { Name = f.Internet.DomainName(), Response = JsonNode.Parse("{ \"result\": \"value\" }") }
: null;
}
diff --git a/tests/GenerativeAI.Microsoft.Tests/Microsoft_AIFunction_Tests.cs b/tests/GenerativeAI.Microsoft.Tests/Microsoft_AIFunction_Tests.cs
index ce7124a9..afde82a5 100644
--- a/tests/GenerativeAI.Microsoft.Tests/Microsoft_AIFunction_Tests.cs
+++ b/tests/GenerativeAI.Microsoft.Tests/Microsoft_AIFunction_Tests.cs
@@ -1,4 +1,5 @@
using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using System.Text.Json;
using System.Text.Json.Nodes;
@@ -7,7 +8,7 @@
using ChatOptions = Microsoft.Extensions.AI.ChatOptions;
using GenerativeAI.Microsoft.Extensions;
using GenerativeAI.Tests;
-using Json.More;
+
using Microsoft.Extensions.AI;
using Newtonsoft.Json;
using Shouldly;
@@ -16,8 +17,14 @@
namespace GenerativeAI.IntegrationTests;
+
+
public class Microsoft_AIFunction_Tests:TestBase
{
+ public Microsoft_AIFunction_Tests(ITestOutputHelper helper) : base(helper)
+ {
+
+ }
[Fact]
public async Task ShouldWorkWithTools()
{
@@ -34,6 +41,23 @@ public async Task ShouldWorkWithTools()
.ShouldBeTrue();
}
+ [Fact]
+ public async Task ShouldWorkWithComplexClasses()
+ {
+ Assert.SkipUnless(IsGeminiApiKeySet,GeminiTestSkipMessage);
+ var apiKey = Environment.GetEnvironmentVariable("GEMINI_API_KEY", EnvironmentVariableTarget.User);
+ var chatClient = new GenerativeAIChatClient(apiKey, modelName:"models/gemini-2.0-flash");
+ var chatOptions = new ChatOptions();
+
+ chatOptions.Tools = new List{AIFunctionFactory.Create(GetStudentRecordAsync)};
+ var message = new ChatMessage(ChatRole.User, "How does student john doe in senior grade is doing this year, enrollment start 01-01-2024 to 01-01-2025?");
+ var response = await chatClient.GetResponseAsync(message,options:chatOptions).ConfigureAwait(false);
+
+ response.Choices.LastOrDefault().Text.Contains("John", StringComparison.InvariantCultureIgnoreCase)
+ .ShouldBeTrue();
+ Console.WriteLine(response.Choices.LastOrDefault().Text);
+ }
+
[Fact]
public async Task ShouldWorkWith_BookStoreService()
{
@@ -71,6 +95,28 @@ public Weather GetCurrentWeather(string location, Unit unit = Unit.Celsius)
};
}
+ [System.ComponentModel.Description("Get student record for the year")]
+
+ public async Task GetStudentRecordAsync(QueryStudentRecordRequest query)
+ {
+ return new StudentRecord
+ {
+ StudentId = "12345",
+ FullName = query.FullName,
+ Level = StudentRecord.GradeLevel.Senior,
+ EnrolledCourses = new List { "Math 101", "Physics 202", "History 303" },
+ Grades = new Dictionary
+ {
+ { "Math 101", 3.5 },
+ { "Physics 202", 3.8 },
+ { "History 303", 3.9 }
+ },
+ EnrollmentDate = new DateTime(2020, 9, 1),
+ IsActive = true
+ };
+ }
+
+
public enum Unit
{
Celsius,
@@ -85,4 +131,50 @@ public class Weather
public Unit Unit { get; set; }
public string Description { get; set; } = string.Empty;
}
+
+ public class StudentRecord
+ {
+ public enum GradeLevel
+ {
+ Freshman,
+ Sophomore,
+ Junior,
+ Senior,
+ Graduate
+ }
+
+ public string StudentId { get; set; } = string.Empty;
+ public string FullName { get; set; } = string.Empty;
+ public GradeLevel Level { get; set; } = GradeLevel.Freshman;
+ public List EnrolledCourses { get; set; } = new List();
+ public Dictionary Grades { get; set; } = new Dictionary();
+ public DateTime EnrollmentDate { get; set; } = DateTime.Now;
+ public bool IsActive { get; set; } = true;
+
+ public double CalculateGPA()
+ {
+ if (Grades.Count == 0) return 0.0;
+ return Grades.Values.Average();
+ }
+ }
+
+ [Description("Request class containing filters for querying student records.")]
+ public class QueryStudentRecordRequest
+ {
+ [Description("The student's full name.")]
+ public string FullName { get; set; } = string.Empty;
+
+ [Description("Grade filters for querying specific grades, e.g., Freshman or Senior.")]
+ public List GradeFilters { get; set; } = new();
+
+ [Description("The start date for the enrollment date range. ISO 8601 standard date")]
+ public DateTime EnrollmentStartDate { get; set; }
+
+ [Description("The end date for the enrollment date range. ISO 8601 standard date")]
+ public DateTime EnrollmentEndDate { get; set; }
+
+ [Description("The flag indicating whether to include only active students.")]
+ public bool? IsActive { get; set; } = true;
+ }
+
}
\ No newline at end of file
diff --git a/tests/GenerativeAI.Microsoft.Tests/Microsoft_ChatClient_Tests.cs b/tests/GenerativeAI.Microsoft.Tests/Microsoft_ChatClient_Tests.cs
index 4d3030f0..c3f23027 100644
--- a/tests/GenerativeAI.Microsoft.Tests/Microsoft_ChatClient_Tests.cs
+++ b/tests/GenerativeAI.Microsoft.Tests/Microsoft_ChatClient_Tests.cs
@@ -195,20 +195,20 @@ public void ShouldReturnSelfFromGetServiceIfTypeMatches()
Console.WriteLine("GetService returned the correct instance when serviceType matches the client type.");
}
- [Fact, TestPriority(8)]
- public void ShouldReturnNullFromGetServiceIfTypeDoesNotMatch()
- {
- // Arrange
- var adapter = GetTestGooglePlatform();
- var client = new GenerativeAIChatClient(adapter);
-
- // Act
- var service = client.GetService(typeof(object));
-
- // Assert
- service.ShouldBeNull();
- Console.WriteLine("GetService returned null when the requested serviceType did not match.");
- }
+ // [Fact, TestPriority(8)]
+ // public void ShouldReturnNullFromGetServiceIfTypeDoesNotMatch()
+ // {
+ // // Arrange
+ // var adapter = GetTestGooglePlatform();
+ // var client = new GenerativeAIChatClient(adapter);
+ //
+ // // Act
+ // var service = client.GetService(typeof(object));
+ //
+ // // Assert
+ // service.ShouldBeNull();
+ // Console.WriteLine("GetService returned null when the requested serviceType did not match.");
+ // }
#endregion
@@ -231,6 +231,6 @@ public void MetadataShouldBeNullByDefault()
protected override IPlatformAdapter GetTestGooglePlatform()
{
Assert.SkipWhen(!IsGeminiApiKeySet, GeminiTestSkipMessage);
- return new GoogleAIPlatformAdapter("sldakfhklash fklasdhklf");
+ return new GoogleAIPlatformAdapter(EnvironmentVariables.GOOGLE_API_KEY);
}
}
\ No newline at end of file
diff --git a/tests/GenerativeAI.SemanticRetrieval.Tests/Clients/RagEngine/VertexRagManager_Tests.cs b/tests/GenerativeAI.SemanticRetrieval.Tests/Clients/RagEngine/VertexRagManager_Tests.cs
index ec7a55cc..d4f36b9f 100644
--- a/tests/GenerativeAI.SemanticRetrieval.Tests/Clients/RagEngine/VertexRagManager_Tests.cs
+++ b/tests/GenerativeAI.SemanticRetrieval.Tests/Clients/RagEngine/VertexRagManager_Tests.cs
@@ -45,6 +45,7 @@ public async Task ShouldCreateCorpusWithPineconeAsync()
{
// Arrange
var client = new VertexRagManager(GetTestVertexAIPlatform(), null);
+ //client.Platform.Authenticator = new GoogleCloudAdcAuthenticator();
var newCorpus = new RagCorpus
{
DisplayName = "Test Pinecone Corpus",
@@ -58,7 +59,8 @@ public async Task ShouldCreateCorpusWithPineconeAsync()
{
IndexName = "test-index-5"
},
- apiKeyResourceName: Environment.GetEnvironmentVariable("pinecone-secret"))
+ apiKeyResourceName:"projects/103876794532/secrets/pinecone/versions/1")
+ //apiKeyResourceName: Environment.GetEnvironmentVariable("pinecone-secret"))
.ConfigureAwait(false);
// Assert
@@ -232,23 +234,29 @@ public async Task ShouldListCorporaAsync()
// Console.WriteLine($"Corpora updated: {updated.DisplayName}, ");
// }
- [Fact, TestPriority(100)]
+ //[Fact(Skip = "Not needed", Explicit = true), TestPriority(100)]
+ [RunnableInDebugOnly]
public async Task ShouldDeleteCorporaAsync()
{
// Arrange
var client = new VertexRagManager(GetTestVertexAIPlatform(), null);
var list = await client.ListCorporaAsync().ConfigureAwait(false);
- var corpusName = list.RagCorpora
- .FirstOrDefault(s => s.DisplayName.Contains("test", StringComparison.OrdinalIgnoreCase)).Name;
-
- // Act
- await client.DeleteRagCorpusAsync(corpusName).ConfigureAwait(false);
+ foreach (var l in list.RagCorpora)
+ {
+ await client.DeleteRagCorpusAsync(l.Name).ConfigureAwait(false);
+
+ }
+ // var corpusName = list.RagCorpora
+ // .FirstOrDefault(s => s.DisplayName.Contains("test", StringComparison.OrdinalIgnoreCase)).Name;
+ //
+ // // Act
+ // await client.DeleteRagCorpusAsync(corpusName).ConfigureAwait(false);
// Assert
// No exception should be thrown
- Console.WriteLine($"Corpus Deleted: {corpusName}");
+ //Console.WriteLine($"Corpus Deleted: {corpusName}");
}
[Fact, TestPriority(7)]
@@ -272,7 +280,8 @@ public async Task ShouldUploadLocalFileAsync()
Console.WriteLine($"Corpus Deleted: {corpusName}");
}
- [Fact(Skip = "Not needed",Explicit = true), TestPriority(7)]
+ //[Fact(Skip = "Not needed",Explicit = true), TestPriority(7)]
+ [Fact, TestPriority(7)]
public async Task ShouldImportFileAsync()
{
// Arrange
@@ -283,11 +292,11 @@ public async Task ShouldImportFileAsync()
.FirstOrDefault(s => s.DisplayName.Contains("test", StringComparison.OrdinalIgnoreCase)).Name;
var request = new ImportRagFilesRequest();
- // request.AddGooglDriveSource(new GoogleDriveSourceResourceId()
- // {
- // ResourceId = "",
- // ResourceType = GoogleDriveSourceResourceIdResourceType.RESOURCE_TYPE_FILE
- // });
+ request.AddGooglDriveSource(new GoogleDriveSourceResourceId()
+ {
+ ResourceId = "",
+ ResourceType = GoogleDriveSourceResourceIdResourceType.RESOURCE_TYPE_FILE
+ });
var file = "TestData/pg1184.txt";
// Act
var result = await client.ImportFilesAsync(corpusName, request).ConfigureAwait(false);
@@ -329,7 +338,8 @@ public async Task ShouldGetFileAsync()
var f = await client.GetFileAsync(last.Name).ConfigureAwait(false);
}
- [Fact(Skip = "Not needed",Explicit = true), TestPriority(7)]
+ //[Fact(Skip = "Not needed",Explicit = true), TestPriority(7)]
+ [Fact, TestPriority(7)]
public async Task ShouldQueryWithCorpusAsync()
{
// Arrange
diff --git a/tests/GenerativeAI.Tests/GenerativeAI.Tests.csproj b/tests/GenerativeAI.Tests/GenerativeAI.Tests.csproj
index 8a781246..470df0e7 100644
--- a/tests/GenerativeAI.Tests/GenerativeAI.Tests.csproj
+++ b/tests/GenerativeAI.Tests/GenerativeAI.Tests.csproj
@@ -1,7 +1,7 @@
- net8.0;net9.0;
+ net8.0;net9.0;net6.0;
enable
enable
latest
diff --git a/tests/GenerativeAI.Tests/Model/GenerativeAI_Multimodal_Tests.cs b/tests/GenerativeAI.Tests/Model/GenerativeAI_Multimodal_Tests.cs
index 3ffb9f44..2fd41fd4 100644
--- a/tests/GenerativeAI.Tests/Model/GenerativeAI_Multimodal_Tests.cs
+++ b/tests/GenerativeAI.Tests/Model/GenerativeAI_Multimodal_Tests.cs
@@ -96,6 +96,34 @@ public async Task ShouldProcessAudioWithFilePath()
// text.ShouldContain("theological", Case.Insensitive);
Console.WriteLine(result.Text());
}
+
+ // [Fact]
+ // public async Task ShouldProcessRemoteFile()
+ // {
+ // //Arrange
+ //
+ // var vertex = GetTestVertexAIPlatform();
+ // //var model = CreateInitializedModel();
+ //
+ // var model = new GeminiModel(vertex, TestModel);
+ // string prompt = "what is this audio about?";
+ //
+ // var request = new GenerateContentRequest();
+ // request.AddRemoteFile("https://storage.googleapis.com/cloud-samples-data/generative-ai/audio/pixel.mp3","audio/mp3");
+ //
+ // //request.AddRemoteFile("https://www.gutenberg.org/cache/epub/1184/pg1184.txt","text/plain");
+ // request.AddText(prompt);
+ // //Act
+ // var result = await model.GenerateContentAsync(request).ConfigureAwait(false);
+ //
+ // //Assert
+ // result.ShouldNotBeNull();
+ // var text = result.Text();
+ // text.ShouldNotBeNull();
+ // // if(!text.Contains("theological",StringComparison.InvariantCultureIgnoreCase) && !text.Contains("Friedrich",StringComparison.InvariantCultureIgnoreCase))
+ // // text.ShouldContain("theological", Case.Insensitive);
+ // Console.WriteLine(result.Text());
+ // }
[Fact]
public async Task ShouldIdentifyImageWithWithStreaming()
diff --git a/tests/GenerativeAI.Tests/Platforms/GooglAIAdapter_Initialization_Tests.cs b/tests/GenerativeAI.Tests/Platforms/GooglAIAdapter_Initialization_Tests.cs
index 9cb302c1..aac2cbab 100644
--- a/tests/GenerativeAI.Tests/Platforms/GooglAIAdapter_Initialization_Tests.cs
+++ b/tests/GenerativeAI.Tests/Platforms/GooglAIAdapter_Initialization_Tests.cs
@@ -110,16 +110,16 @@ public void Constructor_WithLogger_ShouldInitializeLogger()
{
// Arrange
var loggerMock = new Mock();
-
+
// Act
var adapter = new GoogleAIPlatformAdapter("TEST_API_KEY", logger: loggerMock.Object);
-
+
// Assert
// Using reflection because Logger is not public:
var loggerProperty = adapter.GetType().GetProperty("Logger",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
loggerProperty.ShouldNotBeNull("Logger property should exist.");
-
+
var actualLogger = loggerProperty!.GetValue(adapter) as ILogger;
actualLogger.ShouldNotBeNull();
actualLogger.ShouldBe(loggerMock.Object);