21 changed files with 541 additions and 1 deletions
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,24 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net10.0</TargetFramework> |
|||
<AssemblyName>LINGYUN.Abp.ElsaNext.AI</AssemblyName> |
|||
<PackageId>LINGYUN.Abp.ElsaNext.AI</PackageId> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Elsa.AI.Copilot" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.ElsaNext\LINGYUN.Abp.ElsaNext.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,23 @@ |
|||
using Elsa.Features.Services; |
|||
using LINGYUN.Abp.ElsaNext.AI.Extensions; |
|||
using Microsoft.Extensions.Configuration; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.ElsaNext.AI; |
|||
|
|||
[DependsOn(typeof(AbpElsaNextModule))] |
|||
public class AbpElsaNextAIModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Configuration.GetSection("Elsa:AI"); |
|||
PreConfigure<IModule>(elsa => |
|||
{ |
|||
elsa.UseAbpAI(ai => |
|||
{ |
|||
ai.ConfigureOptions = options => configuration.GetSection("Host").Bind(options); |
|||
ai.ConfigureCopilotOptions = options => configuration.GetSection("Copilot").Bind(options); |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
using Elsa.Extensions; |
|||
using Elsa.Features.Services; |
|||
using LINGYUN.Abp.ElsaNext.AI.Features; |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.ElsaNext.AI.Extensions; |
|||
|
|||
public static class ModuleExtensions |
|||
{ |
|||
public static IModule UseAbpAI(this IModule module, Action<AbpAIFeature>? configure = null) |
|||
{ |
|||
return module.Use(configure); |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using Elsa.AI.Copilot.Options; |
|||
using Elsa.AI.Host.Features; |
|||
using Elsa.AI.Host.Options; |
|||
using Elsa.AI.Host.Services; |
|||
using Elsa.Extensions; |
|||
using Elsa.Features.Abstractions; |
|||
using Elsa.Features.Services; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.ElsaNext.AI.Features; |
|||
|
|||
public class AbpAIFeature(IModule module) : FeatureBase(module) |
|||
{ |
|||
public Action<AIHostOptions>? ConfigureOptions { get; set; } |
|||
public Action<CopilotOptions>? ConfigureCopilotOptions { get; set; } |
|||
public Action<AIToolEnablementService>? ConfigureToolEnablement { get; set; } |
|||
|
|||
public override void Configure() |
|||
{ |
|||
Module.AddFastEndpointsAssembly<AIFeature>(); |
|||
} |
|||
|
|||
public override void Apply() |
|||
{ |
|||
Services.AddAIHostServices(ConfigureOptions); |
|||
Services.AddCopilotAIProvider(ConfigureCopilotOptions); |
|||
if (ConfigureToolEnablement != null) |
|||
{ |
|||
Services.AddSingleton(ConfigureToolEnablement); |
|||
} |
|||
Module.AddFastEndpointsFromModule(); |
|||
} |
|||
|
|||
public AbpAIFeature EnableWorkflowProposalTools() |
|||
{ |
|||
ConfigureToolEnablement += enablement => |
|||
{ |
|||
enablement.Enable("workflows.proposeCreate"); |
|||
enablement.Enable("workflows.proposeUpdate"); |
|||
}; |
|||
return this; |
|||
} |
|||
} |
|||
@ -0,0 +1,49 @@ |
|||
using Elsa.Studio.AI.Extensions; |
|||
using LINGYUN.Abp.ElsaNext.AI; |
|||
using LINGYUN.Abp.ElsaNext.Localization; |
|||
using LINGYUN.Abp.ElsaNext.Studio.AI.Blazor.Navigation; |
|||
using LINGYUN.Abp.ElsaNext.Studio.Blazor; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.AspNetCore.Components.Web.Theming.MudBlazor.Routing; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.UI.Navigation; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.ElsaNext.Studio.AI.Blazor; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpElsaNextAIModule), |
|||
typeof(AbpElsaNextStudioBlazorModule))] |
|||
public class AbpElsaNextStudioAIBlazorModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
var configuration = context.Configuration.GetSection("Elsa:Studio"); |
|||
|
|||
Configure<AbpRouterOptions>(options => |
|||
{ |
|||
options.AdditionalAssemblies.Add(typeof(AbpElsaNextStudioAIBlazorModule).Assembly); |
|||
}); |
|||
|
|||
Configure<AbpNavigationOptions>(options => |
|||
{ |
|||
options.MenuContributors.Add(new ElsaStudioAIMenuContributor()); |
|||
}); |
|||
|
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpElsaNextStudioAIBlazorModule>("LINGYUN.Abp.ElsaNext.Studio.AI.Blazor"); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Get<ElsaNextResource>() |
|||
.AddVirtualJson("/Localization/Resources/AIBlazor"); |
|||
}); |
|||
var elsaNextStudioBlazoeOptions = context.Services.ExecutePreConfiguredActions<AbpElsaNextStudioBlazorOptions>(); |
|||
|
|||
context.Services.AddWeaverModule(elsaNextStudioBlazoeOptions.BackendApiConfig); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,31 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Razor"> |
|||
|
|||
<Import Project="..\..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net10.0</TargetFramework> |
|||
<Nullable>enable</Nullable> |
|||
<ImplicitUsings>enable</ImplicitUsings> |
|||
<AssemblyName>LINGYUN.Abp.ElsaNext.Studio.AI.Blazor</AssemblyName> |
|||
<PackageId>LINGYUN.Abp.ElsaNext.Studio.AI.Blazor</PackageId> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<Content Remove="Localization\Resources\AIBlazor\*.json" /> |
|||
<EmbeddedResource Include="Localization\Resources\AIBlazor\*.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Elsa.Studio.AI" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.ElsaNext.AI\LINGYUN.Abp.ElsaNext.AI.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.ElsaNext.Studio.Blazor\LINGYUN.Abp.ElsaNext.Studio.Blazor.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Menu:AI": "AI", |
|||
"AI": "AI" |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Menu:AI": "AI", |
|||
"AI": "AI" |
|||
} |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using LINGYUN.Abp.ElsaNext.Localization; |
|||
using LINGYUN.Abp.ElsaNext.Studio.Blazor.Navigation; |
|||
using Volo.Abp.UI.Navigation; |
|||
|
|||
namespace LINGYUN.Abp.ElsaNext.Studio.AI.Blazor.Navigation; |
|||
|
|||
public class ElsaStudioAIMenuContributor : IMenuContributor |
|||
{ |
|||
public Task ConfigureMenuAsync(MenuConfigurationContext context) |
|||
{ |
|||
if (context.Menu.Name == StandardMenus.Main) |
|||
{ |
|||
var menu = context.Menu; |
|||
var l = context.GetLocalizer<ElsaNextResource>(); |
|||
|
|||
var group = menu.GetMenuItemOrNull(ElsaStudioMenus.GroupName); |
|||
group?.AddItem(new ApplicationMenuItem( |
|||
ElsaStudioMenus.GroupName + ".AI", |
|||
l["Menu:AI"], |
|||
url: "/ai/weaver", |
|||
icon: "fa fa-openai", |
|||
order: 6)); |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
@page "/ai/weaver" |
|||
@attribute [Authorize] |
|||
@inherits StudioComponentBase |
|||
@using Elsa.Studio.AI.UI.Pages |
|||
|
|||
<HeadContent> |
|||
@* <link rel="stylesheet" href="_content/Elsa.Studio.AI/Elsa.Studio.AI.bundle.scp.css" /> *@ |
|||
<link rel="stylesheet" href="_content/LINGYUN.Abp.ElsaNext.Studio.AI.Blazor/weaver.css" /> |
|||
</HeadContent> |
|||
|
|||
<Weaver /> |
|||
@ -0,0 +1,5 @@ |
|||
@using Microsoft.AspNetCore.Authorization |
|||
@using Microsoft.AspNetCore.Components.Web |
|||
@using Elsa.Studio.Components |
|||
@using Elsa.Studio.Localization |
|||
@using MudBlazor |
|||
@ -0,0 +1,196 @@ |
|||
.weaver-page { |
|||
height: calc(100vh - 72px); |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 12px; |
|||
} |
|||
|
|||
.weaver-heading, |
|||
.weaver-thread-header, |
|||
.weaver-panel-title, |
|||
.weaver-composer-actions, |
|||
.weaver-tool, |
|||
.weaver-activity { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
gap: 8px; |
|||
} |
|||
|
|||
.weaver-workspace { |
|||
min-height: 0; |
|||
display: grid; |
|||
grid-template-columns: minmax(0, 1fr) 360px; |
|||
gap: 12px; |
|||
flex: 1; |
|||
} |
|||
|
|||
.weaver-main, |
|||
.weaver-side, |
|||
.weaver-thread { |
|||
min-height: 0; |
|||
} |
|||
|
|||
.weaver-thread, |
|||
.weaver-panel { |
|||
border: 1px solid var(--mud-palette-lines-default); |
|||
border-radius: 8px; |
|||
background: var(--mud-palette-surface); |
|||
} |
|||
|
|||
.weaver-thread { |
|||
height: 100%; |
|||
display: grid; |
|||
grid-template-rows: auto minmax(0, 1fr) auto auto; |
|||
gap: 12px; |
|||
padding: 12px; |
|||
} |
|||
|
|||
.weaver-message-list, |
|||
.weaver-activity-list, |
|||
.weaver-tool-list { |
|||
min-height: 0; |
|||
overflow: auto; |
|||
} |
|||
|
|||
.weaver-message-list { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 10px; |
|||
} |
|||
|
|||
.weaver-message { |
|||
max-width: 78%; |
|||
border: 1px solid var(--mud-palette-lines-default); |
|||
border-radius: 8px; |
|||
padding: 10px 12px; |
|||
background: var(--mud-palette-background); |
|||
} |
|||
|
|||
.weaver-message.is-user { |
|||
align-self: flex-end; |
|||
background: var(--mud-palette-primary-hover); |
|||
} |
|||
|
|||
.weaver-message.is-assistant { |
|||
align-self: flex-start; |
|||
} |
|||
|
|||
.weaver-message-meta { |
|||
font-size: 0.72rem; |
|||
color: var(--mud-palette-text-secondary); |
|||
margin-bottom: 4px; |
|||
} |
|||
|
|||
.weaver-message-text { |
|||
white-space: pre-wrap; |
|||
} |
|||
|
|||
.weaver-empty { |
|||
margin: auto; |
|||
text-align: center; |
|||
} |
|||
|
|||
.weaver-composer { |
|||
border-top: 1px solid var(--mud-palette-lines-default); |
|||
padding-top: 10px; |
|||
display: grid; |
|||
gap: 8px; |
|||
} |
|||
|
|||
.weaver-attachments { |
|||
min-height: 28px; |
|||
flex-wrap: wrap; |
|||
} |
|||
|
|||
.weaver-agent-select { |
|||
width: 160px; |
|||
} |
|||
|
|||
.weaver-context-kind { |
|||
width: 190px; |
|||
} |
|||
|
|||
.weaver-reference-input { |
|||
width: 220px; |
|||
} |
|||
|
|||
.weaver-side { |
|||
display: grid; |
|||
grid-template-rows: auto minmax(160px, 1fr) minmax(120px, 1fr) auto; |
|||
gap: 12px; |
|||
} |
|||
|
|||
.weaver-panel { |
|||
padding: 12px; |
|||
overflow: auto; |
|||
} |
|||
|
|||
.weaver-capability-grid { |
|||
display: grid; |
|||
grid-template-columns: 1fr 1fr; |
|||
gap: 8px; |
|||
margin-top: 10px; |
|||
} |
|||
|
|||
.weaver-capability-pill { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 6px; |
|||
border: 1px solid var(--mud-palette-lines-default); |
|||
border-radius: 6px; |
|||
padding: 6px 8px; |
|||
font-size: 0.82rem; |
|||
} |
|||
|
|||
.weaver-capability-pill.is-enabled { |
|||
color: var(--mud-palette-success); |
|||
} |
|||
|
|||
.weaver-capability-pill.is-disabled { |
|||
color: var(--mud-palette-text-secondary); |
|||
} |
|||
|
|||
.weaver-activity-list, |
|||
.weaver-tool-list { |
|||
display: grid; |
|||
gap: 8px; |
|||
margin-top: 10px; |
|||
} |
|||
|
|||
.weaver-activity, |
|||
.weaver-tool, |
|||
.weaver-proposal { |
|||
border: 1px solid var(--mud-palette-lines-default); |
|||
border-radius: 8px; |
|||
padding: 8px; |
|||
} |
|||
|
|||
@media (max-width: 960px) { |
|||
.weaver-page { |
|||
height: auto; |
|||
} |
|||
|
|||
.weaver-workspace { |
|||
grid-template-columns: 1fr; |
|||
} |
|||
|
|||
.weaver-side { |
|||
grid-template-rows: none; |
|||
} |
|||
|
|||
.weaver-message { |
|||
max-width: 100%; |
|||
} |
|||
|
|||
.weaver-composer-actions { |
|||
align-items: stretch; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.weaver-agent-select, |
|||
.weaver-context-kind, |
|||
.weaver-reference-input { |
|||
width: 100%; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue