diff --git a/LlmTornado.Contrib/LlmTornado.Contrib.csproj b/LlmTornado.Contrib/LlmTornado.Contrib.csproj index 3da2d66..f8259d1 100644 --- a/LlmTornado.Contrib/LlmTornado.Contrib.csproj +++ b/LlmTornado.Contrib/LlmTornado.Contrib.csproj @@ -4,10 +4,33 @@ net8.0 enable enable + true + Contribution addon for LlmTornado + Matěj "lofcz" Štágl, LlmTornado Contributors + MIT + https://github.com/lofcz/LlmTornado + https://github.com/lofcz/LlmTornado + git + llmtornado + nuget_logo.jpg + preview + 1.0.1 + Provides extra functionality to LlmTornado. + + + + + + + true + \ + + + diff --git a/LlmTornado.Contrib/LlmTornadoExtensions.cs b/LlmTornado.Contrib/LlmTornadoExtensions.cs new file mode 100644 index 0000000..2490e70 --- /dev/null +++ b/LlmTornado.Contrib/LlmTornadoExtensions.cs @@ -0,0 +1,146 @@ +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using EnumsNET; +using LlmTornado.ChatFunctions; +using Newtonsoft.Json.Linq; + +namespace LlmTornado.Contrib; + +public static class LlmTornadoExtensions +{ + /// + /// Attempts to get value of a given argument. If the argument is of incompatible type, is returned. + /// + /// + /// + /// + /// + /// + /// + public static bool TryGetArgument(this FunctionCall call, string arg, [NotNullWhen(returnValue: true)] out T? data, out Exception? exception) + { + return Get(call.ArgGetter.Value.Source, arg, out data, out exception); + } + + /// + /// Attempts to get value of a given argument. If the argument is not found or is of incompatible type, null is returned. + /// + /// + /// + /// + /// + /// + public static bool TryGetArgument(this FunctionCall call, string arg, [NotNullWhen(returnValue: true)] out T? data) + { + return Get(call.ArgGetter.Value.Source, arg, out data, out _); + } + + internal static bool Get(this Dictionary? args, string param, out T? data, out Exception? exception) + { + exception = null; + + if (args is null) + { + data = default; + return false; + } + + if (!args.TryGetValue(param, out object? rawData)) + { + data = default; + return false; + } + + if (rawData is T obj) + { + data = obj; + } + + if (rawData is JArray jArr) + { + data = jArr.ToObject(); + return true; + } + + try + { + data = (T?)rawData.ChangeType(typeof(T)); + return true; + } + catch (Exception e) + { + data = default; + exception = e; + return false; + } + } + + public static object? ChangeType(this object? value, Type conversion) + { + Type? t = conversion; + + if (t.IsEnum && value != null) + { + if (Enums.TryParse(t, value.ToString(), true, out object? x)) + { + return x; + } + } + + Type? nullableUnderlyingType = Nullable.GetUnderlyingType(t); + + if (nullableUnderlyingType is not null && value is not null) + { + if (nullableUnderlyingType.IsEnum) + { + if (Enums.TryParse(nullableUnderlyingType, value.ToString(), true, out object? x)) + { + return x; + } + } + } + + if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + if (value == null) + { + return null; + } + + t = Nullable.GetUnderlyingType(t); + } + + if (t == typeof(int) && value?.ToString() == string.Empty) + { + return 0; + } + + if (t == typeof(int) && ((value?.ToString()?.Contains('.') ?? false) || (value?.ToString()?.Contains(',') ?? false))) + { + if (double.TryParse(value.ToString()?.Replace(",", "."), out double x)) + { + return (int)x; + } + } + + if (value is not null && t is { IsGenericType: true } && value.GetType().IsGenericType) + { + Type destT = t.GetGenericArguments()[0]; + Type sourceT = value.GetType().GetGenericArguments()[0]; + + if (destT.IsEnum && sourceT == typeof(int)) + { + IList? instance = (IList?)Activator.CreateInstance(t); + + foreach (object? x in (IList)value) + { + instance?.Add(x); + } + + return instance; + } + } + + return t is not null ? Convert.ChangeType(value, t) : null; + } +} \ No newline at end of file diff --git a/LlmTornado.Contrib/nuget_logo.jpg b/LlmTornado.Contrib/nuget_logo.jpg new file mode 100644 index 0000000..3f55109 Binary files /dev/null and b/LlmTornado.Contrib/nuget_logo.jpg differ diff --git a/LlmTornado.Demo/ChatDemo.cs b/LlmTornado.Demo/ChatDemo.cs index 1d0360c..a8e4a9d 100644 --- a/LlmTornado.Demo/ChatDemo.cs +++ b/LlmTornado.Demo/ChatDemo.cs @@ -8,6 +8,7 @@ using LlmTornado.Code; using LlmTornado.Code.Models; using LlmTornado.Common; +using LlmTornado.Contrib; namespace LlmTornado.Demo; diff --git a/LlmTornado.Demo/LlmTornado.Demo.csproj b/LlmTornado.Demo/LlmTornado.Demo.csproj index c6453ab..415a655 100644 --- a/LlmTornado.Demo/LlmTornado.Demo.csproj +++ b/LlmTornado.Demo/LlmTornado.Demo.csproj @@ -8,6 +8,7 @@ + diff --git a/LlmTornado/Chat/FunctionCall.cs b/LlmTornado/Chat/FunctionCall.cs index c8d7d1d..cb75fe0 100644 --- a/LlmTornado/Chat/FunctionCall.cs +++ b/LlmTornado/Chat/FunctionCall.cs @@ -12,16 +12,25 @@ namespace LlmTornado.ChatFunctions; /// public class FunctionCall { + /// + /// Arguments. + /// [JsonIgnore] - private readonly Lazy argGetter; + internal readonly Lazy ArgGetter; + /// + /// Decoded arguments. + /// [JsonIgnore] - private readonly Lazy?> decodedArguments; + internal readonly Lazy?> DecodedArguments; + /// + /// Creates an empty function call. + /// public FunctionCall() { - argGetter = new Lazy(() => new ChatFunctionParamsGetter(decodedArguments?.Value)); - decodedArguments = new Lazy?>(() => Arguments.IsNullOrWhiteSpace() ? [] : JsonConvert.DeserializeObject>(Arguments)); + ArgGetter = new Lazy(() => new ChatFunctionParamsGetter(DecodedArguments?.Value)); + DecodedArguments = new Lazy?>(() => Arguments.IsNullOrWhiteSpace() ? [] : JsonConvert.DeserializeObject>(Arguments)); } [JsonIgnore] @@ -57,32 +66,7 @@ public FunctionCall() /// public Dictionary GetArguments() { - return argGetter.Value.Source ?? []; - } - - /// - /// Attempts to get value of a given argument. If the argument is of incompatible type, is returned. - /// - /// - /// - /// - /// - /// - public bool TryGetArgument(string param, [NotNullWhen(returnValue: true)] out T? data, out Exception? exception) - { - return argGetter.Value.Get(param, out data, out exception); - } - - /// - /// Attempts to get value of a given argument. If the argument is not found or is of incompatible type, null is returned. - /// - /// - /// - /// - /// - public bool TryGetArgument(string param, [NotNullWhen(returnValue: true)] out T? data) - { - return argGetter.Value.Get(param, out data, out _); + return ArgGetter.Value.Source ?? []; } /// diff --git a/LlmTornado/Chat/Plugins/ChatPluginFunctionInputParams.cs b/LlmTornado/Chat/Plugins/ChatPluginFunctionInputParams.cs index bd6ae3f..b4f21c4 100644 --- a/LlmTornado/Chat/Plugins/ChatPluginFunctionInputParams.cs +++ b/LlmTornado/Chat/Plugins/ChatPluginFunctionInputParams.cs @@ -5,52 +5,22 @@ namespace LlmTornado.Chat.Plugins; +/// +/// Input params of a function call. +/// public class ChatPluginFunctionInputParams { - private readonly Dictionary? source; - + /// + /// Source dictionary. + /// + public Dictionary? Source { get; set; } + + /// + /// Creates an input params from a dictionary. + /// + /// public ChatPluginFunctionInputParams(Dictionary? pars) { - source = pars; - } - - public bool Get(string param, out T? data, out Exception? exception) - { - exception = null; - - if (source == null) - { - data = default; - return false; - } - - if (!source.TryGetValue(param, out object? rawData)) - { - data = default; - return false; - } - - if (rawData is T obj) - { - data = obj; - } - - if (rawData is JArray jArr) - { - data = jArr.ToObject(); - return true; - } - - try - { - data = (T?)rawData.ChangeType(typeof(T)); - return true; - } - catch (Exception e) - { - data = default; - exception = e; - return false; - } + Source = pars; } } \ No newline at end of file diff --git a/LlmTornado/Code/Extensions.cs b/LlmTornado/Code/Extensions.cs index 7a627f3..9568fff 100644 --- a/LlmTornado/Code/Extensions.cs +++ b/LlmTornado/Code/Extensions.cs @@ -1,12 +1,8 @@ -using System; -using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Reflection; -using EnumsNET; using Newtonsoft.Json; namespace LlmTornado.Code; @@ -59,75 +55,6 @@ public static void AddOrUpdate(this ConcurrentDictionary diction return dictionary; } - public static object? ChangeType(this object? value, Type conversion) - { - Type? t = conversion; - - if (t.IsEnum && value != null) - { - if (Enums.TryParse(t, value.ToString(), true, out object? x)) - { - return x; - } - } - - Type? nullableUnderlyingType = Nullable.GetUnderlyingType(t); - - if (nullableUnderlyingType is not null && value is not null) - { - if (nullableUnderlyingType.IsEnum && value != null) - { - if (Enums.TryParse(nullableUnderlyingType, value.ToString(), true, out object? x)) - { - return x; - } - } - } - - if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) - { - if (value == null) - { - return null; - } - - t = Nullable.GetUnderlyingType(t); - } - - if (t == typeof(int) && value?.ToString() == "") - { - return 0; - } - - if (t == typeof(int) && ((value?.ToString()?.Contains('.') ?? false) || (value?.ToString()?.Contains(',') ?? false))) - { - if (double.TryParse(value.ToString()?.Replace(",", "."), out double x)) - { - return (int)x; - } - } - - if (value != null && t is { IsGenericType: true } && value.GetType().IsGenericType) - { - Type destT = t.GetGenericArguments()[0]; - Type sourceT = value.GetType().GetGenericArguments()[0]; - - if (destT.IsEnum && sourceT == typeof(int)) - { - IList? instance = (IList?)Activator.CreateInstance(t); - - foreach (object? x in (IList)value) - { - instance?.Add(x); - } - - return instance; - } - } - - return t != null ? System.Convert.ChangeType(value, t) : null; - } - private static readonly JsonSerializerSettings JsonSettingsIgnoreNulls = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; public static string ToJson(this object? obj, bool prettify = false) diff --git a/LlmTornado/Code/PartialModels.cs b/LlmTornado/Code/PartialModels.cs index a96a964..fc71304 100644 --- a/LlmTornado/Code/PartialModels.cs +++ b/LlmTornado/Code/PartialModels.cs @@ -123,46 +123,6 @@ public ChatFunctionParamsGetter(Dictionary? pars) { Source = pars; } - - public bool Get(string param, [NotNullWhen(returnValue: true)] out T? data, out Exception? exception) - { - exception = null; - - if (Source is null) - { - data = default; - return false; - } - - if (!Source.TryGetValue(param, out object? rawData)) - { - data = default; - return false; - } - - if (rawData is T obj) - { - data = obj; - } - - if (rawData is JArray jArr) - { - data = jArr.ToObject(); - return true; - } - - try - { - data = (T?)rawData.ChangeType(typeof(T)); - return true; - } - catch (Exception e) - { - data = default; - exception = e; - return false; - } - } } internal class ToolCallInboundAccumulator diff --git a/LlmTornado/LlmTornado.csproj b/LlmTornado/LlmTornado.csproj index f5b1228..811096d 100644 --- a/LlmTornado/LlmTornado.csproj +++ b/LlmTornado/LlmTornado.csproj @@ -1,6 +1,6 @@  - latestmajor + preview true Matěj Štágl, LlmTornado contributors LlmTornado @@ -13,7 +13,7 @@ OpenAI NextGeneration safe apis: include content of the request LlmTornado - 3.1.25 + 3.1.26 3.0.5 3.0.5 True @@ -41,12 +41,15 @@ - - - + + + + <_Parameter1>LlmTornado.Contrib + +