<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
  <channel xmlns:media="http://search.yahoo.com/mrss/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <title>ABP.IO Stories</title>
    <link>https://abp.io/community/articles</link>
    <description>A hub for ABP Framework, .NET, and software development. Access articles, tutorials, news, and contribute to the ABP community.</description>
    <lastBuildDate>Wed, 21 Oct 2026 13:45:27 Z</lastBuildDate>
    <generator>Community - ABP.IO</generator>
    <image>
      <url>https://abp.io/assets/favicon.ico/favicon-32x32.png</url>
      <title>ABP.IO Stories</title>
      <link>https://abp.io/community/articles</link>
    </image>
    <a10:link rel="self" type="application/rss+xml" title="self" href="https://abp.io/community/rss?member=arif" />
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/multiworkspace-management-for-abp-applications-eghgty3j</guid>
      <link>https://abp.io/community/posts/multiworkspace-management-for-abp-applications-eghgty3j</link>
      <a10:author>
        <a10:name>arif</a10:name>
        <a10:uri>https://abp.io/community/members/arif</a10:uri>
      </a10:author>
      <title>Multi-Workspace Management for ABP Applications</title>
      <description>The workspace system implements data isolation within your company similar to ABP's multi-tenancy, but at a more granular level —just like you see in platforms such as GitHub, Bitbucket, or Trello, where you can create different repositories or boards under different workspaces.</description>
      <pubDate>Thu, 22 May 2025 07:18:04 Z</pubDate>
      <a10:updated>2026-08-11T14:16:15Z</a10:updated>
      <content:encoded><![CDATA[<h1 style="display: flex; align-items: center;">
  Multi-Workspace Management for ABP Applications
</h1>
<p>In this article, we show how you can separate your application's resources by workspaces—just like you see in platforms such as GitHub, Bitbucket, or Trello, where you can create different repositories or boards under different workspaces.</p>
<p>We implemented workspace management in a generic and reusable way, inspired by how ABP implements tenant management. Just as ABP automatically filters and separates data for each tenant, our approach does the same for workspaces—making it easy to keep data isolated and organized.</p>
<h2>✅ Demo</h2>
<p>In this sample, we created three offices as workspaces:</p>
<ul>
<li>Wafi Solutions BD</li>
<li>Wafi Solutions UK</li>
<li>Wafi Solutions KSA</li>
</ul>
<p>We then created employees under each office. This implementation demonstrates how easily you can manage and separate resources (like employees) under different workspaces.</p>
<p><img src="https://raw.githubusercontent.com/WafiSolutions/AbpApps/main/modules/AbpWorkspace/etc/img/workspace.gif" alt="Demo" /></p>
<p>After completing these steps, your ABP application will have a fully functioning workspace management system that:</p>
<ol>
<li>Isolates data between workspaces</li>
<li>Provides workspace management UI</li>
<li>Handles workspace-based routing</li>
<li>Works seamlessly with ABP's existing features</li>
</ol>
<p>Users can:</p>
<ul>
<li>Create new workspaces</li>
<li>Switch between workspaces</li>
<li>Manage employees under workspace.</li>
</ul>
<h2>🏗️ Architecture Overview</h2>
<p>The solution consists of two primary modules:</p>
<ol>
<li><p><strong><code>Wafi.Abp.Workspaces.Core</code></strong>
Core module that provides the fundamental workspace management capabilities:</p>
<ul>
<li>Workspace entity definitions and interfaces</li>
<li>Middleware for workspace resolution from HTTP headers</li>
<li>EF Core integration through custom DbContext</li>
<li>Data filtering based on current workspace</li>
</ul>
</li>
<li><p><strong><code>Wafi.Abp.Workspaces.Web</code></strong>
UI components and controllers for workspace management in ABP web applications:</p>
<ul>
<li>Workspace selector UI components</li>
<li>HTTP interceptors for automatic workspace header injection</li>
<li>Management interface for workspace configuration</li>
</ul>
</li>
</ol>
<h2>🔧 How It Works</h2>
<p>The workspace system implements data isolation within your company similar to ABP's multi-tenancy, but at a more granular level - enabling teams, departments, or projects to have their own isolated workspaces within the same tenant.</p>
<h3>Core Components and Their Interactions</h3>
<ol>
<li><strong>Entity Integration with <code>IWorkspace.cs</code></strong>:
This simple interface marks entities that should be segregated by workspace. Any entity implementing <code>IWorkspace</code> will automatically be filtered by the current workspace context.</li>
</ol>
<pre><code class="language-csharp">// IWorkspace interface definition
public interface IWorkspace
{
    Guid? WorkspaceId { get; set; }
}
</code></pre>
<ol start="2">
<li><strong>HTTP Request Processing with <code>WorkspaceResolutionMiddleware.cs</code></strong>:
This ASP.NET Core middleware intercepts all incoming requests to determine the current workspace context. It works in the request pipeline to:
<ul>
<li>Extract workspace information from HTTP headers</li>
<li>Set the current workspace for the duration of the request</li>
<li>Apply workspace context to all downstream operations</li>
</ul>
</li>
</ol>
<pre><code class="language-csharp">// Key part of the middleware
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
    var workspaceResolveContext = new WorkspaceResolveContext(context);

    // Try resolving workspace from various sources
    foreach (var workspaceResolver in _options.WorkspaceResolvers)
    {
        await workspaceResolver.ResolveAsync(workspaceResolveContext);
        if (workspaceResolveContext.WorkspaceId.HasValue) break;
    }

    // Apply workspace context for the request duration
    if (workspaceResolveContext.WorkspaceId.HasValue)
    {
        using (_currentWorkspace.Change(workspaceResolveContext.WorkspaceId.Value))
        {
            await next(context);
        }
    }
    else
    {
        await next(context);
    }
}
</code></pre>
<ol start="3">
<li><strong>Header-Based Resolution with <code>WorkspaceIdHeaderResolveContributor.cs</code></strong>:
This component extracts the workspace ID from the HTTP header (<code>X-Workspace-Id</code>), enabling seamless workspace resolution for API requests.</li>
</ol>
<pre><code class="language-csharp">// Workspace ID Header Resolver
public class WorkspaceIdHeaderResolveContributor : IWorkspaceResolveContributor, ITransientDependency
{
    public const string HeaderName = &quot;X-Workspace-Id&quot;;
    public const string ContributorName = &quot;WorkspaceIdHeader&quot;;

    public Task ResolveAsync(IWorkspaceResolveContext context)
    {
        var httpContext = context.GetHttpContext();
        if (httpContext == null) return Task.CompletedTask;

        var workspaceIdHeader = httpContext.Request.Headers[HeaderName];
        if (workspaceIdHeader.Count == 0 || string.IsNullOrWhiteSpace(workspaceIdHeader[0]))
            return Task.CompletedTask;

        if (Guid.TryParse(workspaceIdHeader[0], out var workspaceId))
        {
            context.WorkspaceId = workspaceId;
        }
        
        return Task.CompletedTask;
    }
}
</code></pre>
<ol start="4">
<li><strong>Data Filtering with <code>WorkspaceDbContextBase.cs</code></strong>:
This specialized DbContext provides automatic data filtering for all workspace-aware entities by:
<ul>
<li>Automatically applying the current workspace ID to new entities</li>
<li>Preventing accidental workspace ID modifications</li>
<li>Filtering query results to only show data from the current workspace</li>
</ul>
</li>
</ol>
<pre><code class="language-csharp">// Simplified version of WorkspaceDbContextBase filter expression
protected override Expression&lt;Func&lt;TEntity, bool&gt;&gt; CreateFilterExpression&lt;TEntity&gt;(ModelBuilder modelBuilder)
{
    // Get base filter from ABP
    var baseExpression = base.CreateFilterExpression&lt;TEntity&gt;(modelBuilder);
    
    // Skip if entity doesn't implement IWorkspace
    if (!typeof(IWorkspace).IsAssignableFrom(typeof(TEntity)))
    {
        return baseExpression;
    }

    // Find the workspace ID property
    var prop = modelBuilder.Entity&lt;TEntity&gt;()
        .Metadata.FindProperty(nameof(IWorkspace.WorkspaceId))!;
    
    var columnName = prop.GetColumnName() ?? prop.Name;

    // Create workspace filter: only show entities from current workspace
    Expression&lt;Func&lt;TEntity, bool&gt;&gt; workspaceFilter = e =&gt;
        !IsMultiWorkspaceFilterEnabled
        || CurrentWorkspace.Id == null
        || EF.Property&lt;Guid?&gt;(e, columnName) == CurrentWorkspace.Id;

    // Combine with existing filters
    if (baseExpression == null) return workspaceFilter;
    return QueryFilterExpressionHelper.CombineExpressions(baseExpression, workspaceFilter);
}
</code></pre>
<ol start="5">
<li><strong>Client-Side Integration with <code>http-interceptor.js</code></strong>:
This JavaScript component automatically includes the current workspace ID in all AJAX requests, ensuring seamless workspace context propagation from UI to API calls.</li>
</ol>
<pre><code class="language-javascript">// Automatically included from http-interceptor.js
function getWorkspaceId() {
    return localStorage.getItem('selectedWorkspaceId');
}

jQuery(document).ajaxSend(function (event, xhr, settings) {
    const workspaceId = getWorkspaceId();
    if (workspaceId) {
        xhr.setRequestHeader('X-Workspace-Id', workspaceId);
    }
});
</code></pre>
<p>The system integrates seamlessly with ABP's existing multi-tenancy, allowing:</p>
<ul>
<li>Multi-tenant applications with workspace isolation within each tenant</li>
<li>Single-tenant applications with departmental/team separation</li>
<li>Combinations where some data is tenant-specific and some is workspace-specific</li>
</ul>
<h2>⚙️ Implementation Steps</h2>
<p>Follow these steps to integrate Workspaces into your ABP application.</p>
<hr />
<h3>🔹 Step 1: Add the Workspace in your Entities</h3>
<ol>
<li>Add the Workspace packages in domain layer:</li>
</ol>
<pre><code class="language-bash">dotnet add package Wafi.Abp.Workspaces.Core
</code></pre>
<ol start="2">
<li>Add the module dependencies in your modules:</li>
</ol>
<pre><code class="language-csharp">[DependsOn(
    typeof(WafiAbpWorkspaceModule)
)]
public class YourAppDomainModule : AbpModule
{
    // ...
}
</code></pre>
<ol start="3">
<li>Make Your Entities Workspace-Aware</li>
</ol>
<p>Use the <code>IWorkspace</code> interface in your entities where you want to enable the workspace feature. This is very similar to IMultitenant, making integration straightforward:</p>
<pre><code class="language-csharp">// IWorkspace interface definition
public interface IWorkspace
{
    Guid? WorkspaceId { get; set; }
}

// Example implementation
public class YourEntity : FullAuditedAggregateRoot&lt;Guid&gt;, IMultiTenant, IWorkspace
{
    public Guid? TenantId { get; set; }
    public Guid? WorkspaceId { get; set; }
    
    // Your existing entity properties
}
</code></pre>
<h3>🔹 Step 2: Configure Your Entity Framework Core Context</h3>
<ol>
<li>Change the base class of your <code>DbContext</code> from <code>AbpDbContext</code> to <code>WorkspaceDbContextBase</code>.</li>
<li>Add <code>builder.ConfigureWorkspaces();</code> in the <code>OnModelCreating</code> method to register workspace-related entities and configurations.</li>
<li><strong>Run a new migration and update the database</strong> to apply the necessary schema changes.</li>
</ol>
<pre><code class="language-csharp">public class YourAppDbContext : WorkspaceDbContextBase&lt;YourAppDbContext&gt;
{
    public YourAppDbContext(DbContextOptions&lt;YourAppDbContext&gt; options) 
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        // Register workspace-related entity mappings and configurations
        builder.ConfigureWorkspaces();

        // Your existing model configuration
    }
}
</code></pre>
<p>The <code>WorkspaceDbContextBase</code> handles:</p>
<ul>
<li>Applying the current workspace ID to new entities</li>
<li>Preventing workspace ID modifications in existing entities</li>
<li>Filtering queries based on the current workspace context</li>
</ul>
<pre><code class="language-csharp">// Example of SaveChanges method in WorkspaceDbContextBase
public override int SaveChanges(bool acceptAllChangesOnSuccess)
{
    ApplyCurrentWorkspaceId();
    return base.SaveChanges(acceptAllChangesOnSuccess);
}

private void ApplyCurrentWorkspaceId()
{
    if (CurrentWorkspace?.Id == null) return;

    var currentWorkspaceId = CurrentWorkspace.Id.Value;

    foreach (var entry in ChangeTracker.Entries()
        .Where(e =&gt;
            e.Entity is IWorkspace &amp;&amp;
            (e.State == EntityState.Added || e.State == EntityState.Modified)))
    {
        // Stamp the FK column via EF Core API
        entry.Property(nameof(IWorkspace.WorkspaceId)).CurrentValue = currentWorkspaceId;

        if (entry.State == EntityState.Modified)
        {
            // Prevent accidental overwrites
            entry.Property(nameof(IWorkspace.WorkspaceId)).IsModified = false;
        }
    }
}
</code></pre>
<hr />
<h3>🔹 Step 3: Set Up Workspace Management UI</h3>
<ol>
<li>Add the Workspace packages in web layer:</li>
</ol>
<pre><code class="language-bash">dotnet add package Wafi.Abp.Workspaces.Web
</code></pre>
<ol start="2">
<li>Add the module dependencies in your modules:</li>
</ol>
<pre><code class="language-csharp">[DependsOn(
    typeof(WorkspacesWebModule)
)]
public class YourWebModule : AbpModule
{
    // ...
}
</code></pre>
<ol start="3">
<li>Add the Workspace Selector in the leptonx topbar container:</li>
</ol>
<pre><code class="language-csharp">public override void ConfigureServices(ServiceConfigurationContext context)
{
    public class YourToolbarContributor : IToolbarContributor
    {
        public async Task ConfigureToolbarAsync(IToolbarConfigurationContext context)
        {
            if (context.Toolbar.Name == LeptonXLiteToolbars.Main)
            {
                if (context.ServiceProvider.GetRequiredService&lt;ICurrentUser&gt;().IsAuthenticated)
                {
                    context.Toolbar.Items.AddFirst(new ToolbarItem(typeof(WorkspaceSelectorViewComponent)));
                }
            }
        }
    }
}
</code></pre>
<blockquote>
<p>The workspace selector will now appear in your application's topbar, providing:</p>
<ul>
<li>A dropdown to switch between workspaces</li>
<li>Visual indication of the current workspace</li>
<li>Quick access to workspace management</li>
<li>Seamless integration with ABP's LeptonXLite theme</li>
</ul>
</blockquote>
<hr />
<h2>📚 Additional Resources</h2>
<p>For a deeper understanding of the module's implementation, architecture decisions, and advanced customization options, see:</p>
<ul>
<li><a href="https://github.com/WafiSolutions/AbpApps/tree/main/modules/AbpWorkspace">Full Module Source Code</a></li>
<li><a href="https://github.com/WafiSolutions/AbpApps/tree/main/samples/Workspace">Sample Project</a></li>
</ul>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a1a0908-13c0-69c3-5aa1-bf32d898029d" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a1a0908-13c0-69c3-5aa1-bf32d898029d" medium="image" />
    </item>
    <item>
      <guid isPermaLink="true">https://abp.io/community/posts/using-semantic-kernel-in-the-abp-framework-qo5cnuzs</guid>
      <link>https://abp.io/community/posts/using-semantic-kernel-in-the-abp-framework-qo5cnuzs</link>
      <a10:author>
        <a10:name>arif</a10:name>
        <a10:uri>https://abp.io/community/members/arif</a10:uri>
      </a10:author>
      <category>ai</category>
      <title>Using Semantic Kernel in the ABP Framework</title>
      <description>This guide demonstrates how to integrate Microsoft's Semantic Kernel SDK with the ABP.io framework. It includes a wrapper implementation to help you create AI-powered plugins for your ABP application services in a clean, modular way.</description>
      <pubDate>Wed, 07 May 2025 07:40:08 Z</pubDate>
      <a10:updated>2026-10-21T06:27:45Z</a10:updated>
      <content:encoded><![CDATA[<h1 style="display: flex; align-items: center;">
  <img src="https://img.icons8.com/color/48/microsoft.png" alt="Microsoft Logo" style="vertical-align: middle; margin-right: 10px;">&nbsp;
  Using Semantic Kernel in an ABP Framework
</h1>
<p>This guide demonstrates how to integrate <strong>Microsoft's Semantic Kernel SDK</strong> with the <strong>ABP.io</strong> framework. It includes a wrapper implementation to help you create AI-powered plugins for your ABP application services in a clean, modular way.</p>
<h2>🏗️ Architecture Overview</h2>
<p>The solution consists of two primary modules:</p>
<ol>
<li><p><strong><code>WafiOpenAISemanticKernelModule</code></strong>
Core module that provides integration with Semantic Kernel.</p>
</li>
<li><p><strong><code>WafiSmartHRAIPluginModule</code></strong>
Sample implementation module demonstrating how to create AI plugins for business entities.</p>
</li>
</ol>
<h2>⚙️ Implementation Steps</h2>
<p>Follow these steps to integrate Semantic Kernel into your ABP application.</p>
<hr />
<h3>🔹 Step 1: Add the Package to Your HttpApi Project</h3>
<ol>
<li>Add the Semantic Kernel wrapper package:</li>
</ol>
<pre><code class="language-bash">dotnet add YourApp.HttpApi.csproj package Wafi.Abp.OpenAISemanticKernel
</code></pre>
<ol start="2">
<li>Add the module dependency and configure Semantic Kernel options:
This connects your ABP module with the Semantic Kernel framework and sets up OpenAI credentials.</li>
</ol>
<pre><code class="language-csharp">[DependsOn(
    // your existing dependencies
    typeof(WafiOpenAISemanticKernelModule)
)]
public class YourAppHttpApiModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        var configuration = context.Services.GetConfiguration();

        Configure&lt;WafiOpenAISemanticKernelOptions&gt;(options =&gt;
        {
            options.ModelId = configuration.GetValue&lt;string&gt;(&quot;SemanticKernel:OpenAI:ModelId&quot;);
            options.ApiKey = configuration.GetValue&lt;string&gt;(&quot;SemanticKernel:OpenAI:ApiKey&quot;);
        });
    }
}
</code></pre>
<ol start="3">
<li>Add the following configuration to your appsettings.json:</li>
</ol>
<pre><code>{
  &quot;SemanticKernel&quot;: {
    &quot;OpenAI&quot;: {
      &quot;ApiKey&quot;: &quot;your-openai-key&quot;,
      &quot;ModelId&quot;: &quot;gpt-4&quot;
    }
  }
}
</code></pre>
<blockquote>
<p>✅ This exposes the <code>/api/OpenAISemanticKernel/ai/ask</code> endpoint automatically, which the chat interface will use to interact with Semantic Kernel.</p>
</blockquote>
<hr />
<h3>🔹 Step 2: Implement a Chat Interface</h3>
<p>In your Web project, create a chat interface to interact with the <code>/ai/ask</code> API. You can refer to the host/<code>Wafi.SmartHR.Web</code> project for a sample implementation.</p>
<p><img src="https://raw.githubusercontent.com/WafiSolutions/AbpApps/main/etc/img/folder-chat-page.gif" alt="chat-page-location" /></p>
<hr />
<h3>🔹 Step 3: Build Plugins to Connect ABP Services with Semantic Kernel</h3>
<h4>1. Add dependencies to the plugin project:</h4>
<pre><code class="language-bash">dotnet add package Wafi.Abp.OpenAISemanticKernel
dotnet add reference ../YourApp.Application/YourApp.Application.csproj
</code></pre>
<p>Add this to your plugin module class:</p>
<pre><code class="language-csharp">[DependsOn(typeof(YourAppApplicationModule))]
public class YourAppAIPluginModule : AbpModule
{
    // ...
}
</code></pre>
<h4>2. Create AI Plugins for Your Entities</h4>
<p>Each plugin class should clearly describe what the AI will get when calling its methods.</p>
<p>📄 Example: <code>YourEntityPlugin.cs</code></p>
<pre><code class="language-csharp">public class YourEntityPlugin : ApplicationService, ITransientDependency
{
    private readonly IYourEntityAppService _entityService;
    private readonly IAuthorizationService _authorizationService;

    public YourEntityPlugin(
        IYourEntityAppService entityService,
        IAuthorizationService authorizationService)
    {
        _entityService = entityService;
        _authorizationService = authorizationService;
    }

    [KernelFunction]
    [Description(&quot;Search for entities by name or any relevant property using a keyword or phrase&quot;)]
    public async Task&lt;string&gt; SearchEntitiesAsync(string filter)
    {
        if (!await _authorizationService.IsGrantedAsync(YourPermissions.Default))
        {
            return &quot;You are not authorized to access this data&quot;;
        }

        var entities = await _entityService.GetListAsync(filter);
        
        if (entities is null || entities.Items.Count == 0)
        {
            return $&quot;No entities found for filter '{filter}'&quot;;
        }

        return JsonSerializer.Serialize(entities);
    }
}
</code></pre>
<blockquote>
<p>🧠 Be sure to give a meaningful <code>[Description]</code> so that the AI knows exactly what this method does.</p>
</blockquote>
<h4>3. Create a Plugin Provider</h4>
<p>Create a provider class to register your plugin with Semantic Kernel. This is a crucial step that makes your plugin discoverable by the AI system.</p>
<pre><code class="language-csharp">using Wafi.Abp.OpenAISemanticKernel.Plugins;
using YourApp.AI.Plugin.YourEntities;

namespace YourApp.AI.Plugin
{
    public class YourEntityPluginProvider : SemanticPluginProviderBase&lt;YourEntityPlugin&gt;, IWafiPluginProvider
    {
        public YourEntityPluginProvider(YourEntityPlugin plugin) : base(plugin) { }

        public override string Name =&gt; &quot;Your Plugin Name&quot;;
    }
}
</code></pre>
<h4>🔐 Authorization</h4>
<p>All requests are protected by ABP's permission system. If a user doesn't have the required permissions, they will receive an appropriate message:</p>
<blockquote>
<p>⚠️ <strong>You are not authorized to access this data.</strong></p>
</blockquote>
<p>The authorization check is performed in each plugin method:</p>
<pre><code class="language-csharp">if (!await _authorizationService.IsGrantedAsync(YourPermissions.Default))
{
    return &quot;You are not authorized to access this data&quot;;
}
</code></pre>
<hr />
<h2>✅ Final Outcome</h2>
<p>After completing these steps, your ABP application will have a fully functioning AI assistant powered by Semantic Kernel.</p>
<p><img src="https://raw.githubusercontent.com/WafiSolutions/AbpApps/main/etc/img/chat_sample.gif" alt="Demo" /></p>
<p>The AI interface can be accessed through the <code>/askai</code> endpoint in your web application. When users send questions to this endpoint, the system:</p>
<ol>
<li>Processes the natural language query</li>
<li>Determines which registered plugins can fulfill the request</li>
<li>Calls the appropriate application services through the plugins</li>
<li>Returns formatted results to the user</li>
</ol>
<h4>Example 1: Employee List Query</h4>
<p><strong>Request:</strong></p>
<pre><code class="language-http">POST /askai
Content-Type: application/json

{
    &quot;question&quot;: &quot;Give me the list of employees&quot;
}
</code></pre>
<p><strong>Response:</strong></p>
<blockquote>
<p>Here is the list of employees: <strong>John Doe</strong>   - Email: john.doe@example.com   - Phone: 1234567890   - Date of Birth: January 1, 1990   - Joining Date: January 1, 2020   - Total Leave Days: 20   - Remaining Leave Days: 20. <strong>Jane Smith</strong>   - Email: jane.smith@example.com   - Phone: 0987654321   - Date of Birth: February 1, 1991   - Joining Date: February 1, 2021   - Total Leave Days: 20   - Remaining Leave Days: 15</p>
</blockquote>
<h4>Example 2: Leave Records Query</h4>
<p><strong>Request:</strong></p>
<pre><code class="language-http">POST /askai
Content-Type: application/json

{
    &quot;question&quot;: &quot;Give me the list of employee leave records&quot;
}
</code></pre>
<p><strong>Response:</strong></p>
<blockquote>
<p>Here is the list of employee leave records: <strong>John Doe</strong>   - Annual leave from January 1 to January 5, 2025 (5 days)  - Sick leave from March 15 to March 16, 2025 (2 days)\n   - Personal leave from June 1 to June 3, 2025 (3 days) <strong>Jane Smith</strong>   - Sick leave from February 1 to February 3, 2025 (3 days)   - Annual leave from July 1 to July 10, 2025 (10 days)&quot;</p>
</blockquote>
<hr />
<h2>📚 Additional Resources</h2>
<p>For a deeper understanding of the module's implementation, architecture decisions, and advanced customization options, see:</p>
<ul>
<li><a href="https://learn.microsoft.com/en-us/semantic-kernel/overview/">Microsoft Semantic Kernel Documentation</a></li>
<li><a href="https://docs.abp.io/en/abp/latest/">ABP Framework Documentation</a></li>
<li><a href="https://github.com/WafiSolutions/AbpApps/tree/main/modules/OpenAISemanticKernel">Full Module Source Code</a></li>
</ul>
]]></content:encoded>
      <media:thumbnail url="https://abp.io/api/posts/cover-picture-source/3a19bbdc-e1fd-dca2-83f4-df76545fdaaa" />
      <media:content url="https://abp.io/api/posts/cover-picture-source/3a19bbdc-e1fd-dca2-83f4-df76545fdaaa" medium="image" />
    </item>
  </channel>
</rss>