Browse Source

feat(ai): Add the "IsGlobal" field to the AITool

pull/1457/head
colin 6 months ago
parent
commit
4890f4c271
  1. 383
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327071051_Add-IsGlobal-To-AITool-Record.Designer.cs
  2. 29
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327071051_Add-IsGlobal-To-AITool-Record.cs
  3. 3
      aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs
  4. 4
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinition.cs
  5. 8
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolFactory.cs
  6. 22
      aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs
  7. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordCreateOrUpdateDto.cs
  8. 2
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordDto.cs
  9. 6
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionAppService.cs
  10. 22
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionRecord.cs
  11. 11
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionSerializer.cs
  12. 8
      aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/DynamicAIToolDefinitionStoreInMemoryCache.cs

383
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327071051_Add-IsGlobal-To-AITool-Record.Designer.cs

@ -0,0 +1,383 @@
// <auto-generated />
using System;
using LINGYUN.Abp.MicroService.AIService;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
using Volo.Abp.EntityFrameworkCore;
#nullable disable
namespace LINGYUN.Abp.MicroService.AIService.Migrations
{
[DbContext(typeof(AIServiceMigrationsDbContext))]
[Migration("20260327071051_Add-IsGlobal-To-AITool-Record")]
partial class AddIsGlobalToAIToolRecord
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql)
.HasAnnotation("ProductVersion", "10.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.ConversationRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("CreationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uuid")
.HasColumnName("CreatorId");
b.Property<DateTime>("ExpiredAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uuid")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("character varying(50)");
b.Property<Guid?>("TenantId")
.HasColumnType("uuid")
.HasColumnName("TenantId");
b.Property<DateTime?>("UpdateAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("Workspace")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.HasKey("Id");
b.ToTable("AbpAIConversations", (string)null);
});
modelBuilder.Entity("LINGYUN.Abp.AIManagement.Chats.TextChatMessageRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<string>("Content")
.IsRequired()
.HasMaxLength(1024)
.HasColumnType("character varying(1024)");
b.Property<Guid?>("ConversationId")
.HasColumnType("uuid");
b.Property<DateTime>("CreatedAt")
.HasColumnType("timestamp with time zone");
b.Property<DateTime>("CreationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uuid")
.HasColumnName("CreatorId");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("text")
.HasColumnName("ExtraProperties");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uuid")
.HasColumnName("LastModifierId");
b.Property<DateTime?>("ReplyAt")
.HasColumnType("timestamp with time zone");
b.Property<string>("ReplyMessage")
.HasColumnType("text");
b.Property<string>("Role")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<Guid?>("TenantId")
.HasColumnType("uuid")
.HasColumnName("TenantId");
b.Property<Guid?>("UserId")
.HasColumnType("uuid");
b.Property<string>("Workspace")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.HasKey("Id");
b.HasIndex("TenantId", "ConversationId");
b.ToTable("AbpAITextChatMessages", (string)null);
});
modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tokens.TokenUsageRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<long?>("CachedInputTokenCount")
.HasColumnType("bigint");
b.Property<Guid?>("ConversationId")
.HasColumnType("uuid");
b.Property<DateTime>("CreationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uuid")
.HasColumnName("CreatorId");
b.Property<long?>("InputTokenCount")
.HasColumnType("bigint");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uuid")
.HasColumnName("LastModifierId");
b.Property<Guid?>("MessageId")
.HasColumnType("uuid");
b.Property<long?>("OutputTokenCount")
.HasColumnType("bigint");
b.Property<long?>("ReasoningTokenCount")
.HasColumnType("bigint");
b.Property<Guid?>("TenantId")
.HasColumnType("uuid")
.HasColumnName("TenantId");
b.Property<long?>("TotalTokenCount")
.HasColumnType("bigint");
b.HasKey("Id");
b.HasIndex("TenantId", "ConversationId");
b.ToTable("AbpAITokenUsages", (string)null);
});
modelBuilder.Entity("LINGYUN.Abp.AIManagement.Tools.AIToolDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uuid")
.HasColumnName("CreatorId");
b.Property<string>("Description")
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("text")
.HasColumnName("ExtraProperties");
b.Property<bool>("IsEnabled")
.HasColumnType("boolean");
b.Property<bool>("IsGlobal")
.HasColumnType("boolean");
b.Property<bool>("IsSystem")
.HasColumnType("boolean");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uuid")
.HasColumnName("LastModifierId");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<string>("Provider")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("StateCheckers")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpAIAIToolDefinitionRecords", (string)null);
});
modelBuilder.Entity("LINGYUN.Abp.AIManagement.Workspaces.WorkspaceDefinitionRecord", b =>
{
b.Property<Guid>("Id")
.HasColumnType("uuid");
b.Property<string>("ApiBaseUrl")
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("ApiKey")
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.IsRequired()
.HasMaxLength(40)
.HasColumnType("character varying(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("uuid")
.HasColumnName("CreatorId");
b.Property<string>("Description")
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("DisplayName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.Property<string>("ExtraProperties")
.IsRequired()
.HasColumnType("text")
.HasColumnName("ExtraProperties");
b.Property<float?>("FrequencyPenalty")
.HasColumnType("real");
b.Property<string>("Instructions")
.HasMaxLength(512)
.HasColumnType("character varying(512)");
b.Property<bool>("IsEnabled")
.HasColumnType("boolean");
b.Property<bool>("IsSystem")
.HasColumnType("boolean");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("timestamp with time zone")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("uuid")
.HasColumnName("LastModifierId");
b.Property<int?>("MaxOutputTokens")
.HasColumnType("integer");
b.Property<string>("ModelName")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(64)
.HasColumnType("character varying(64)");
b.Property<float?>("PresencePenalty")
.HasColumnType("real");
b.Property<string>("Provider")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("character varying(20)");
b.Property<string>("StateCheckers")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("SystemPrompt")
.HasMaxLength(512)
.HasColumnType("character varying(512)");
b.Property<float?>("Temperature")
.HasColumnType("real");
b.Property<string>("Tools")
.HasMaxLength(128)
.HasColumnType("character varying(128)");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("AbpAIWorkspaceDefinitions", (string)null);
});
#pragma warning restore 612, 618
}
}
}

29
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/20260327071051_Add-IsGlobal-To-AITool-Record.cs

@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LINGYUN.Abp.MicroService.AIService.Migrations
{
/// <inheritdoc />
public partial class AddIsGlobalToAIToolRecord : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsGlobal",
table: "AbpAIAIToolDefinitionRecords",
type: "boolean",
nullable: false,
defaultValue: false);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsGlobal",
table: "AbpAIAIToolDefinitionRecords");
}
}
}

3
aspnet-core/aspire/LINGYUN.Abp.MicroService.AIService.EntityFrameworkCore/Migrations/AIServiceMigrationsDbContextModelSnapshot.cs

@ -232,6 +232,9 @@ namespace LINGYUN.Abp.MicroService.AIService.Migrations
b.Property<bool>("IsEnabled") b.Property<bool>("IsEnabled")
.HasColumnType("boolean"); .HasColumnType("boolean");
b.Property<bool>("IsGlobal")
.HasColumnType("boolean");
b.Property<bool>("IsSystem") b.Property<bool>("IsSystem")
.HasColumnType("boolean"); .HasColumnType("boolean");

4
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolDefinition.cs

@ -30,6 +30,10 @@ public class AIToolDefinition : IHasSimpleStateCheckers<AIToolDefinition>
/// 启用/禁用工具 /// 启用/禁用工具
/// </summary> /// </summary>
public bool IsEnabled { get; set; } public bool IsEnabled { get; set; }
/// <summary>
/// 全局工具
/// </summary>
public bool IsGlobal { get; set; }
public List<ISimpleStateChecker<AIToolDefinition>> StateCheckers { get; } public List<ISimpleStateChecker<AIToolDefinition>> StateCheckers { get; }

8
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AIToolFactory.cs

@ -22,8 +22,12 @@ public class AIToolFactory : IAIToolFactory, IScopedDependency
AIToolProviderManager = aIToolProviderManager; AIToolProviderManager = aIToolProviderManager;
} }
public virtual Task<AITool[]> CreateTool(AIToolDefinition definition) public async virtual Task<AITool[]> CreateTool(AIToolDefinition definition)
{ {
if (!definition.IsEnabled)
{
return [];
}
foreach (var provider in AIToolProviderManager.Providers) foreach (var provider in AIToolProviderManager.Providers)
{ {
if (!string.Equals(provider.Name, definition.Provider)) if (!string.Equals(provider.Name, definition.Provider))
@ -31,7 +35,7 @@ public class AIToolFactory : IAIToolFactory, IScopedDependency
continue; continue;
} }
return provider.CreateToolsAsync(definition); return await provider.CreateToolsAsync(definition);
} }
throw new AbpException($"The AITool provider implementation named {definition.Provider} was not found!"); throw new AbpException($"The AITool provider implementation named {definition.Provider} was not found!");

22
aspnet-core/modules/ai/LINGYUN.Abp.AI.Tools/LINGYUN/Abp/AI/Tools/AbpAIToolsModule.cs

@ -36,24 +36,28 @@ public class AbpAIToolsModule : AbpModule
options.ChatClientBuildActions.Add(async (workspace, sp, builder) => options.ChatClientBuildActions.Add(async (workspace, sp, builder) =>
{ {
var useAITools = new List<AITool>(); var useAITools = new List<AITool>();
var useAIToolDefinitions = new List<AIToolDefinition>();
var aiToolFactory = sp.GetRequiredService<IAIToolFactory>(); var aiToolFactory = sp.GetRequiredService<IAIToolFactory>();
var aiToolDefinitionManager = sp.GetRequiredService<IAIToolDefinitionManager>();
var aiToolDefinitions = await aiToolDefinitionManager.GetAllAsync();
if (workspace.Tools.Count > 0) if (workspace.Tools.Count > 0)
{ {
var aiToolDefinitionManager = sp.GetRequiredService<IAIToolDefinitionManager>(); useAIToolDefinitions.AddRange(aiToolDefinitions.Where(aiTool => workspace.Tools.Contains(aiTool.Name)));
var aiToolDefinitions = await aiToolDefinitionManager.GetAllAsync(); }
var useAIToolDefinitions = aiToolDefinitions.Where(aiTool => workspace.Tools.Contains(aiTool.Name)); else
{
useAIToolDefinitions.AddRange(aiToolDefinitions.Where(aiTool => aiTool.IsGlobal));
}
foreach (var aiToolDefinition in aiToolDefinitions) foreach (var aiToolDefinition in aiToolDefinitions)
{
var aiTools = await aiToolFactory.CreateTool(aiToolDefinition);
if (aiTools.Length > 0)
{ {
var aiTools = await aiToolFactory.CreateTool(aiToolDefinition);
useAITools.AddRange(aiTools); useAITools.AddRange(aiTools);
} }
} }
else
{
useAITools.AddRange(await aiToolFactory.CreateAllTools());
}
builder.ConfigureOptions(ai => builder.ConfigureOptions(ai =>
{ {

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordCreateOrUpdateDto.cs

@ -14,6 +14,8 @@ public abstract class AIToolDefinitionRecordCreateOrUpdateDto : ExtensibleObject
public bool IsEnabled { get; set; } public bool IsEnabled { get; set; }
public bool IsGlobal { get; set; }
[DynamicStringLength(typeof(AIToolDefinitionRecordConsts), nameof(AIToolDefinitionRecordConsts.MaxStateCheckersLength))] [DynamicStringLength(typeof(AIToolDefinitionRecordConsts), nameof(AIToolDefinitionRecordConsts.MaxStateCheckersLength))]
public string? StateCheckers { get; set; } public string? StateCheckers { get; set; }
} }

2
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application.Contracts/LINGYUN/Abp/AIManagement/Tools/Dtos/AIToolDefinitionRecordDto.cs

@ -17,6 +17,8 @@ public class AIToolDefinitionRecordDto : ExtensibleAuditedEntityDto<Guid>, IHasC
public bool IsSystem { get; set; } public bool IsSystem { get; set; }
public bool IsGlobal { get; set; }
public string? StateCheckers { get; set; } public string? StateCheckers { get; set; }
public string ConcurrencyStamp { get; set; } public string ConcurrencyStamp { get; set; }

6
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Application/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionAppService.cs

@ -117,6 +117,7 @@ public class AIToolDefinitionAppService :
createInput.StateCheckers) createInput.StateCheckers)
{ {
IsEnabled = createInput.IsEnabled, IsEnabled = createInput.IsEnabled,
IsGlobal = createInput.IsGlobal,
}; };
return record; return record;
@ -134,6 +135,11 @@ public class AIToolDefinitionAppService :
entity.IsEnabled = updateInput.IsEnabled; entity.IsEnabled = updateInput.IsEnabled;
} }
if (entity.IsGlobal != updateInput.IsGlobal)
{
entity.IsGlobal = updateInput.IsGlobal;
}
if (!entity.HasSameExtraProperties(updateInput)) if (!entity.HasSameExtraProperties(updateInput))
{ {
entity.ExtraProperties.Clear(); entity.ExtraProperties.Clear();

22
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionRecord.cs

@ -17,6 +17,8 @@ public class AIToolDefinitionRecord : AuditedAggregateRoot<Guid>
public bool IsSystem { get; set; } public bool IsSystem { get; set; }
public bool IsGlobal { get; set; }
public string? StateCheckers { get; set; } public string? StateCheckers { get; set; }
protected AIToolDefinitionRecord() protected AIToolDefinitionRecord()
{ {
@ -59,6 +61,16 @@ public class AIToolDefinitionRecord : AuditedAggregateRoot<Guid>
return false; return false;
} }
if (IsSystem != otherAITool.IsSystem)
{
return false;
}
if (IsGlobal != otherAITool.IsGlobal)
{
return false;
}
if (StateCheckers != otherAITool.StateCheckers) if (StateCheckers != otherAITool.StateCheckers)
{ {
return false; return false;
@ -94,6 +106,16 @@ public class AIToolDefinitionRecord : AuditedAggregateRoot<Guid>
IsEnabled = otherAITool.IsEnabled; IsEnabled = otherAITool.IsEnabled;
} }
if (IsSystem != otherAITool.IsSystem)
{
IsSystem = otherAITool.IsSystem;
}
if (IsGlobal != otherAITool.IsGlobal)
{
IsGlobal = otherAITool.IsGlobal;
}
if (StateCheckers != otherAITool.StateCheckers) if (StateCheckers != otherAITool.StateCheckers)
{ {
StateCheckers = otherAITool.StateCheckers; StateCheckers = otherAITool.StateCheckers;

11
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/AIToolDefinitionSerializer.cs

@ -45,17 +45,18 @@ public class AIToolDefinitionSerializer : IAIToolDefinitionSerializer, ITransien
definition.Name, definition.Name,
definition.Provider, definition.Provider,
definition.Description != null ? LocalizableStringSerializer.Serialize(definition.Description) : null, definition.Description != null ? LocalizableStringSerializer.Serialize(definition.Description) : null,
SerializeStateCheckers(definition.StateCheckers)); SerializeStateCheckers(definition.StateCheckers))
{
IsGlobal = definition.IsGlobal,
IsEnabled = definition.IsEnabled,
IsSystem = true,
};
foreach (var property in definition.Properties) foreach (var property in definition.Properties)
{ {
aiToolRecord.SetProperty(property.Key, property.Value); aiToolRecord.SetProperty(property.Key, property.Value);
} }
aiToolRecord.IsEnabled = definition.IsEnabled;
aiToolRecord.IsSystem = true;
return Task.FromResult(aiToolRecord); return Task.FromResult(aiToolRecord);
} }
} }

8
aspnet-core/modules/ai/LINGYUN.Abp.AIManagement.Domain/LINGYUN/Abp/AIManagement/Tools/DynamicAIToolDefinitionStoreInMemoryCache.cs

@ -39,9 +39,11 @@ public class DynamicAIToolDefinitionStoreInMemoryCache : IDynamicAIToolDefinitio
var toolDef = new AIToolDefinition( var toolDef = new AIToolDefinition(
tool.Name, tool.Name,
tool.Provider, tool.Provider,
!tool.Description.IsNullOrWhiteSpace() ? LocalizableStringSerializer.Deserialize(tool.Description) : null); !tool.Description.IsNullOrWhiteSpace() ? LocalizableStringSerializer.Deserialize(tool.Description) : null)
{
toolDef.IsEnabled = tool.IsEnabled; IsEnabled = tool.IsEnabled,
IsGlobal = tool.IsGlobal,
};
if (!tool.StateCheckers.IsNullOrWhiteSpace()) if (!tool.StateCheckers.IsNullOrWhiteSpace())
{ {

Loading…
Cancel
Save