< Summary

Information
Class: UIBlazor.Agents.BuiltInToolDefs
Assembly: UIBlazor
File(s): /home/runner/work/InvAit/InvAit/UIBlazor/Agents/BuiltInToolDefs.cs
Tag: 71_26091983037
Line coverage
90%
Covered lines: 104
Uncovered lines: 11
Coverable lines: 115
Total lines: 235
Line coverage: 90.4%
Branch coverage
82%
Covered branches: 69
Total branches: 84
Branch coverage: 82.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ReadFiles(...)100%210%
ApplyDiff(...)100%11100%
MapMethodToTool(...)100%1010100%
MapTypeToProperty(...)85.29%353492%
GetBaseType(...)100%2222100%
IsNullableProperty(...)100%22100%
ToCamelCase(...)50%2266.66%
InvokeToolAsync()35.71%161480%

File(s)

/home/runner/work/InvAit/InvAit/UIBlazor/Agents/BuiltInToolDefs.cs

#LineLine coverage
 1using System.Collections;
 2using System.ComponentModel;
 3using System.Reflection;
 4
 5namespace UIBlazor.Agents;
 6
 7public class BuiltInToolDefs
 8{
 9    [Description("Request to read the contents of one or more files.")]
 10    public Task<string> ReadFiles([Description("File information")] ReadFileParams[] filePath)
 11    {
 12        // тут чтение файлов
 013        return Task.FromResult("");
 14    }
 15
 16    [Description("Applies a series of Search & Replace edits to the specified file.")]
 17    public Task<string> ApplyDiff(
 18        [Description("File path")] string filePath,
 19        [Description("List of pairs 'search/replace'. Executed sequentially.")] DiffEdit[] edits)
 20    {
 21        // тут применение изменений
 122        return Task.FromResult("1234");
 23    }
 24
 25    public static NativeToolDefinition MapMethodToTool(MethodInfo? method)
 26    {
 14527        ArgumentNullException.ThrowIfNull(method);
 14528        var methodDesc = method.GetCustomAttribute<DescriptionAttribute>()?.Description ?? string.Empty;
 29
 14530        var tool = new NativeToolDefinition
 14531        {
 14532            Function = new NativeToolFunction
 14533            {
 14534                Name = method.Name,
 14535                Description = methodDesc,
 14536                Parameters = new NativeParameters
 14537                {
 14538                    Type = NativeToolType.Object,
 14539                    Properties = [],
 14540                    Required = []
 14541                }
 14542            }
 14543        };
 44
 14545        var parameters = tool.Function.Parameters;
 46
 75247        foreach (var param in method.GetParameters())
 48        {
 23149            var paramDesc = param.GetCustomAttribute<DescriptionAttribute>()?.Description ?? string.Empty;
 23150            var paramName = ToCamelCase(param.Name!);
 51
 52            // В Strict Mode все параметры в required, но для optional - тип с null
 23153            var prop = MapTypeToProperty(param.ParameterType, paramDesc, param.HasDefaultValue);
 23154            parameters.Properties.Add(paramName, prop);
 23155            parameters.Required.Add(paramName);
 56        }
 57
 14558        return tool;
 59    }
 60
 61    private static NativePropertyDefinition MapTypeToProperty(Type type, string? description = null, bool isOptional = f
 62    {
 81863        var prop = new NativePropertyDefinition { Description = description };
 64
 65        // Обработка Nullable<T> и nullable reference types
 81866        var underlyingType = Nullable.GetUnderlyingType(type);
 81867        if (underlyingType != null)
 68        {
 6669            var innerProp = MapTypeToProperty(underlyingType, description, true);
 6670            return innerProp;
 71        }
 72
 73        // Обработка массивов и коллекций
 75274        if (type.IsArray || (typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string)))
 75        {
 13176            prop.SetSingleType(NativeToolType.Array);
 13177            var elementType = type.IsArray
 13178                ? type.GetElementType()
 13179                : type.GetGenericArguments().FirstOrDefault();
 80
 13181            var arrayObjects = MapTypeToProperty(elementType ?? typeof(object));
 13182            prop.Items = string.Equals(arrayObjects.Type.ToString(), NativeToolType.Object, StringComparison.Ordinal)
 13183                ? new NativeParameters
 13184                {
 13185                    Type = NativeToolType.Object,
 13186                    Properties = arrayObjects.Properties!,
 38787                    Required = [.. arrayObjects.Properties!.Select(p => p.Key)]
 13188                }
 13189                : arrayObjects;
 90
 13191            if (isOptional)
 92            {
 093                prop.SetUnionTypes(NativeToolType.Array, NativeToolType.Null);
 94            }
 13195            return prop;
 96        }
 97
 98        // Обработка объектов (классы, кроме string и примитивов)
 62199        if (type.IsClass && type != typeof(string))
 100        {
 130101            prop.SetSingleType(NativeToolType.Object);
 130102            prop.Properties = [];
 103
 1040104            foreach (var p in type.GetProperties())
 105            {
 390106                var pDesc = p.GetCustomAttribute<DescriptionAttribute>()?.Description;
 390107                var propName = ToCamelCase(p.Name);
 108
 109                // Проверяем, имеет ли свойство дефолтное значение или nullable
 390110                var hasDefaultValue = p.GetCustomAttribute<DefaultValueAttribute>() != null;
 390111                var isNullableProperty = IsNullableProperty(p);
 390112                var isPropOptional = hasDefaultValue || isNullableProperty;
 390113                prop.Properties.Add(propName, MapTypeToProperty(p.PropertyType, pDesc, isPropOptional));
 114            }
 115
 130116            if (isOptional)
 117            {
 0118                prop.SetUnionTypes(NativeToolType.Object, NativeToolType.Null);
 119            }
 130120            return prop;
 121        }
 122
 123        // Обработка enum
 491124        if (type.IsEnum)
 125        {
 1126            var enumDescription = description;
 1127            if (!string.IsNullOrEmpty(enumDescription))
 0128                enumDescription += " ";
 1129            enumDescription += $"Possible values: {string.Join(", ", Enum.GetNames(type))}";
 130
 1131            prop.Description = enumDescription;
 1132            prop.SetSingleType(NativeToolType.String);
 133
 1134            if (isOptional)
 135            {
 0136                prop.SetUnionTypes(NativeToolType.String, NativeToolType.Null);
 137            }
 1138            return prop;
 139        }
 140
 141        // Обработка примитивов
 490142        var baseType = GetBaseType(type);
 490143        prop.SetSingleType(baseType);
 144
 490145        if (isOptional)
 146        {
 70147            prop.SetUnionTypes(baseType, NativeToolType.Null);
 148        }
 149
 490150        return prop;
 151    }
 152
 153    private static string GetBaseType(Type type)
 154    {
 155        return type switch
 156        {
 695157            _ when type == typeof(int) || type == typeof(long) || type == typeof(short) || type == typeof(byte) => Nativ
 288158            _ when type == typeof(bool) => NativeToolType.Boolean,
 288159            _ when type == typeof(double) || type == typeof(float) || type == typeof(decimal) => NativeToolType.Number,
 278160            _ when type == typeof(DateTime) || type == typeof(DateTimeOffset) => NativeToolType.String,
 276161            _ when type == typeof(Guid) => NativeToolType.String,
 272162            _ => NativeToolType.String,
 163        };
 164    }
 165
 166    private static bool IsNullableProperty(PropertyInfo property)
 167    {
 168        // Проверка Nullable<T>
 390169        if (Nullable.GetUnderlyingType(property.PropertyType) != null)
 65170            return true;
 171
 172        // Проверка nullable reference type через аннотации
 325173        var nullabilityContext = new NullabilityInfoContext();
 325174        var nullabilityInfo = nullabilityContext.Create(property);
 325175        return nullabilityInfo.WriteState == NullabilityState.Nullable;
 176    }
 177
 178    private static string ToCamelCase(string name)
 179    {
 623180        if (string.IsNullOrEmpty(name))
 0181            return name;
 623182        return char.ToLowerInvariant(name[0]) + name.Substring(1);
 183    }
 184
 185    public async Task<string> InvokeToolAsync(string methodName, string argumentsJson)
 186    {
 187        // Находим метод по имени (кейс-сенситив или нет - на ваше усмотрение)
 1188        var method = typeof(BuiltInToolDefs).GetMethod(methodName);
 1189        if (method == null)
 0190            return $"Error: Method {methodName} not found.";
 191
 192        // Настраиваем десериализатор
 1193        var options = new JsonSerializerOptions
 1194        {
 1195            PropertyNameCaseInsensitive = true,
 1196            // Converters = { new JsonStringEnumConverter() }
 1197        };
 198
 1199        var parameters = method.GetParameters();
 1200        var args = new object?[parameters.Length];
 201
 202        // Парсим JSON один раз в документ, чтобы разобрать по параметрам
 1203        using var doc = JsonDocument.Parse(argumentsJson);
 1204        var root = doc.RootElement;
 205
 6206        for (var i = 0; i < parameters.Length; i++)
 207        {
 2208            var p = parameters[i];
 2209            var jsonName = ToCamelCase(p.Name!);
 210
 2211            if (root.TryGetProperty(jsonName, out var propertyElement))
 212            {
 213                // Десериализуем конкретный параметр в нужный тип
 2214                args[i] = JsonSerializer.Deserialize(propertyElement.GetRawText(), p.ParameterType, options);
 215            }
 0216            else if (p.HasDefaultValue)
 217            {
 0218                args[i] = p.DefaultValue;
 219            }
 220            else
 221            {
 222                // В Strict Mode это не должно случиться, но для безопасности:
 0223                args[i] = null;
 224            }
 225        }
 226
 227        // Вызываем метод (предполагаем, что они все Task<string>)
 1228        var result = method.Invoke(this, args);
 229
 1230        if (result is Task<string> task)
 1231            return await task;
 232
 0233        return result?.ToString() ?? string.Empty;
 1234    }
 235}