An answer would be appreciated.
AI agent answer is not relevant for us :)
I’m currently exploring the new AI-related features in ABP based on the following documentation pages:
https://abp.io/docs/10.0/framework/infrastructure/artificial-intelligence
https://abp.io/docs/10.0/modules/ai-management
I understand that these AI modules are currently in preview, and that’s perfectly fine for our use case.
I have a few concrete questions:
Is there anything specific we need to do or enable in order to start using and testing these AI modules in a real (but controlled) project?
Are there any important current limitations or “known gaps” we should be aware of when adopting the preview (e.g. missing scenarios, not-yet-implemented providers, configuration restrictions)?
What is the current roadmap for these AI modules?
Are there specific milestones or upcoming features you can already share (for example around agent support, orchestration, management UI, etc.)?
Do you expect significant breaking changes in the APIs/configuration as the modules evolve, or do you aim to keep the current abstractions relatively stable?
We have a concrete customer scenario:
The customer wants us to build AI agents using ABP-based application.
At the same time, the customer is building their own API endpoint so they can host and manage AI agents themselves.
Our application should act as a client to that external AI/agent API.
In this context, we would like to know:
Can we configure a custom chat client that calls this external API endpoint using the ConfigureChatClient configuration?
If yes:
What is the recommended way to plug such a custom chat client into the AI infrastructure / AI Management module so that it behaves similarly to the built-in providers?
Do you have a minimal example (or guidance) for implementing a custom chat client that forwards requests to an arbitrary HTTP endpoint?
Our goal is to leverage the ABP AI abstractions (for consistency and future maintainability), while letting the actual AI/agent logic run in the customer’s own API.
Alex, this might help?:
https://github.com/face-it/Hangfire.Tags
Thanks for the answer.
Nope, this does not help. Actually: how only the create (before the background job fires), the update is not visible in the audit log.
using (currentPrincipalAccessor.Change(newPrincipal))
{
using (var uow = unitOfWorkManager.Begin(requiresNew: true))
{
using (var auditingScope = auditingManager.BeginScope())
{
configuration = await configurationRepository.GetAsync(Convert.ToInt64(args.ProjectInfo?.ConfigurationId));
var advicefileTask = UploadFileAsync(new RemoteStreamContent(new MemoryStream(pdfAdviceResponseTask.Result), adviceFileName));
var billOfMaterialsFileTask = UploadFileAsync(new RemoteStreamContent(new MemoryStream(pdfBillOfMaterialsResponseTask.Result), BillOfMaterialsFileName));
var anchoringZonesFileTask = UploadFileAsync(new RemoteStreamContent(new MemoryStream(Encoding.UTF8.GetBytes(anchoringZonesSVGTask.Result)), L["DownloadAnchoringDrawing:Filename", args.Configurator.RoofLayoutInformation.TypeOfRoof.Value.ToString()]));
var zonesFileTask = UploadFileAsync(new RemoteStreamContent(new MemoryStream(Encoding.UTF8.GetBytes(zonesSVGTask.Result)), L["DownloadZoneDrawing:Filename", args.Configurator.RoofLayoutInformation.TypeOfRoof.Value.ToString()]));
Task.WaitAll(advicefileTask, billOfMaterialsFileTask, anchoringZonesFileTask, zonesFileTask);
args.Context?.WriteLine($"Started configuring files to database...'");
configuration.AdvicePDFId = advicefileTask.Result.Id;
configuration.BillOfMaterialsPDFId = billOfMaterialsFileTask.Result.Id; // TODO: aanpassen naar billofmaterials
configuration.AnchoringDrawingId = anchoringZonesFileTask.Result.Id;
configuration.ZoneDrawingId = zonesFileTask.Result.Id;
await configurationRepository.UpdateAsync(configuration);
await auditingScope.SaveAsync();
await uow.CompleteAsync();
}
}
}
Thanks for the response,
Now a log rule is fired, but with no properties. Even if i reload the configuration, within the scope, first.
using (currentPrincipalAccessor.Change(newPrincipal))
{
using (var auditingScope = auditingManager.BeginScope())
{
configuration = await configurationRepository.GetAsync(Convert.ToInt64(args.ProjectInfo?.ConfigurationId));
var advicefileTask = UploadFileAsync(new RemoteStreamContent(new MemoryStream(pdfAdviceResponseTask.Result), adviceFileName));
var billOfMaterialsFileTask = UploadFileAsync(new RemoteStreamContent(new MemoryStream(pdfBillOfMaterialsResponseTask.Result), BillOfMaterialsFileName));
var anchoringZonesFileTask = UploadFileAsync(new RemoteStreamContent(new MemoryStream(Encoding.UTF8.GetBytes(anchoringZonesSVGTask.Result)), L["DownloadAnchoringDrawing:Filename", args.Configurator.RoofLayoutInformation.TypeOfRoof.Value.ToString()]));
var zonesFileTask = UploadFileAsync(new RemoteStreamContent(new MemoryStream(Encoding.UTF8.GetBytes(zonesSVGTask.Result)), L["DownloadZoneDrawing:Filename", args.Configurator.RoofLayoutInformation.TypeOfRoof.Value.ToString()]));
Task.WaitAll(advicefileTask, billOfMaterialsFileTask, anchoringZonesFileTask, zonesFileTask);
args.Context?.WriteLine($"Started configuring files to database...'");
configuration.AdvicePDFId = advicefileTask.Result.Id;
configuration.BillOfMaterialsPDFId = billOfMaterialsFileTask.Result.Id;
configuration.AnchoringDrawingId = anchoringZonesFileTask.Result.Id;
configuration.ZoneDrawingId = zonesFileTask.Result.Id;
await configurationRepository.UpdateAsync(configuration);
await auditingScope.SaveAsync();
}
}
This is not the answer.
example deleted
private void ConfigureAuditing() { Configure<AbpAuditingOptions>(options => { options.IsEnabledForAnonymousUsers = false;
options.EntityHistorySelectors.Add(
new NamedTypeSelector(
"MySelectorName",
type =>
{
return type == typeof(Project) || type == typeof(Configuration);
}
)
);
});
}
Currently am trying to add an external login, using Azure Active Directory. The purpose of this external login, is to be used by administrators only.
As described in the community blog post, I did the following... Note the CustomOpenIdConnectEvents class...
private void ConfigureExternalProviders(ServiceConfigurationContext context, IConfiguration configuration)
{
context.Services.AddAuthentication()
//Omitted other third party configurations
.AddOpenIdConnect("AzureOpenId", "Sign in with Microsoft", options =>
{
options.Authority = $"https://login.microsoftonline.com/{configuration["AzureAd:TenantId"]}/v2.0/";
options.ClientId = configuration["AzureAd:ClientId"];
options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
options.CallbackPath = configuration["AzureAd:CallbackPath"];
options.ClientSecret = configuration["AzureAd:ClientSecret"];
options.RequireHttpsMetadata = false;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("email");
options.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "sub");
options.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
options.SignInScheme = IdentityConstants.ExternalScheme;
options.EventsType = typeof(CustomOpenIdConnectEvents);
});
}
And here is the CustomOpenIdConnectEvents class:
{
[UnitOfWork]
public override async Task TokenValidated(TokenValidatedContext context)
{
await base.TokenValidated(context);
var userManager = context.HttpContext.RequestServices.GetRequiredService<IdentityUserManager>();
var userEmail = context.Principal.FindFirstValue(ClaimTypes.Email);
var currentUser = await userManager.FindByEmailAsync(userEmail);
if (currentUser != null)
{
var adminRoleName = "admin"; // Replace with your admin role name
var isInRole = await userManager.IsInRoleAsync(currentUser, adminRoleName);
if (!isInRole)
{
await userManager.AddToRoleAsync(currentUser, adminRoleName);
var identity = context.Principal.Identity as ClaimsIdentity;
identity?.AddClaim(new Claim(identity.RoleClaimType, adminRoleName));
}
}
}
}
The code itself is working. However with the following issues:
var currentUser = await userManager.GetUserAsync(context.Principal);Concrete: How can I make sure users are automatically administrator? Preferably the registration form should be skipped.