Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 9, 2025

Adds the ability for users to create and manage custom AI chat instances with their own configurations, without relying on predefined AI Profiles.

New Capabilities

  • Custom instance configuration: Connection, deployment, system instructions, temperature, top P, frequency/presence penalty, max tokens, past messages count
  • Tool selection: Users can enable/disable available AI tools per instance
  • Instance management: Create, edit, delete multiple named instances per user
  • Conversation history: Context maintained across messages within a session using ChatRole typed messages

Architecture

Following existing codebase patterns:

  • AICustomChatInstance model inheriting from SourceCatalogEntry with YesSql index and migrations
  • ICustomChatInstanceCatalog extending ISourceCatalog<> with user-scoped queries
  • DefaultCustomChatInstanceManager extending SourceCatalogManager<> for CRUD operations
  • CustomChatController using IDisplayManager<AICustomChatInstance> pattern (matching ProfilesController)
  • AICustomChatInstanceDisplayDriver for rendering editor and display shapes
  • Extended AIChatHub with SendCustomChatMessage SignalR method supporting conversation history
  • Vue.js custom-chat.js for real-time chat interface
  • AICustomChatConstants.Permissions.ManageOwnCustomChatInstances permission with admin menu integration

Key Files

  • src/Modules/CrestApps.OrchardCore.AI.Chat/Models/AICustomChatInstance.cs
  • src/Modules/CrestApps.OrchardCore.AI.Chat/Controllers/CustomChatController.cs
  • src/Modules/CrestApps.OrchardCore.AI.Chat/Drivers/AICustomChatInstanceDisplayDriver.cs
  • src/Modules/CrestApps.OrchardCore.AI.Chat/Services/CustomChatInstanceCatalog.cs
  • src/Modules/CrestApps.OrchardCore.AI.Chat/Hubs/AIChatHub.cs
  • src/Modules/CrestApps.OrchardCore.AI.Chat/Views/CustomChat/
Original prompt

Description:

In the CrestApps.OrchardCore.AI.Chat project, we currently have a UI (AIChatSessionChat.cshtml) that allows users to chat with any AI model predefined or configured through an AI Profile. This UI is handled by the AdminController.Index action. It works well for users who can create AI Profiles.

We now need to extend the project to allow users with proper permissions to create custom AI chat instances by providing their own configurations, without relying on predefined AI Profiles.

Requirements:

  1. Custom Chat Configuration:

    • Users must be able to configure all standard AI Profile settings when creating a chat instance:

      • Connection
      • Deployment
      • System instructions
      • Max response
      • Temperature
      • Top P
      • Frequency penalty
      • Presence penalty
      • Include past messages
    • When AIToolDefinitionOptions.Tools has values, users should be able to select which tools to use.

    • Users should also be able to attach files for the AI to analyze.

  2. Chat Instance Management:

    • Users should be able to create multiple chat instances, each with its own configuration.
    • Each chat instance must be saved for reuse.
    • By default, a new chat instance should use the default connection and deployment, with no tools selected.
    • Users should be able to assign a title to each instance.
  3. UI Layout:

    • The UI should have two tabs:

      • Settings tab: for configuring the AI chat instance.
      • Chat tab: for interacting with the AI.
    • Display a list of all chat instances the user has created.

    • Provide a button to create a new chat instance.

  4. References for Implementation:

    • For configuring AI Profile settings, refer to:

      CrestApps.OrchardCore.AI.Tools.Drivers.AIProfileToolsDisplayDriver.AIProfileDisplayDriver
    • Use this as a guide for allowing users to select connections, deployments, tools, and other AI options.

Goal:
Enable users to create and manage fully customizable AI chat instances with a flexible UI that separates settings from chat interactions, while maintaining the ability to save and reuse instances.


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI changed the title [WIP] Add ability to create custom AI chat instances Add custom AI chat instances feature Dec 9, 2025
Copilot AI requested a review from MikeAlhayek December 9, 2025 21:32
}

var instances = await _instanceManager.GetAllAsync();
var instanceList = instances.ToList();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for the extra mutation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d1d26ff - combined the GetAllAsync and ToList calls into a single statement.

}

var instance = await _instanceManager.NewAsync();
var model = await PopulateViewModelAsync(instance, isNew: true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where we can use display manager too.

/// <summary>
/// Represents a custom AI chat instance with user-defined configuration.
/// </summary>
public sealed class AICustomChatInstance : Entity
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe here we should inherit from AIProfile instead and follow the same things we do for AIProfile here in the CustomChat. We may want to also rename the Custom Chat to CustomAIProfile too.

/// <summary>
/// Service for managing custom AI chat instances.
/// </summary>
public interface ICustomChatInstanceManager
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can instead implement ICatalogManager and register SourceCatalogManager<>

The store will register SourceDocumentCatalog<> to use reusable code

@MikeAlhayek
Copy link
Member

@copilot retry the work.

- Model: AICustomChatInstance now inherits from SourceCatalogEntry
- Index: AICustomChatInstanceIndex now inherits from CatalogItemIndex
- Catalog: New ICustomChatInstanceCatalog and CustomChatInstanceCatalog
- Manager: DefaultCustomChatInstanceManager now extends SourceCatalogManager
- Controller: CustomChatController now uses IDisplayManager pattern
- Display Driver: New AICustomChatInstanceDisplayDriver
- Views: Updated to use DisplayManager shapes and EditCatalogEntryViewModel

Co-authored-by: MikeAlhayek <[email protected]>
Copy link
Contributor Author

Copilot AI commented Dec 10, 2025

@copilot retry the work.

Completed the refactoring in e7b2bc4:

  1. AICustomChatInstance now inherits from SourceCatalogEntry
  2. Implemented ICustomChatInstanceCatalog extending ISourceCatalog<> with user-scoped queries
  3. DefaultCustomChatInstanceManager now extends SourceCatalogManager<>
  4. CustomChatController now uses IDisplayManager<AICustomChatInstance> pattern like ProfilesController
  5. Added AICustomChatInstanceDisplayDriver for rendering editor/display shapes
  6. Updated views to use EditCatalogEntryViewModel and ListSourceCatalogEntryViewModel<> patterns
  7. Index now inherits from CatalogItemIndex with ISourceAwareIndex and IDisplayTextAwareIndex

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • nuget.cloudsmith.io
    • Triggering command: /usr/bin/dotnet dotnet build src/Modules/CrestApps.OrchardCore.AI.Chat/CrestApps.OrchardCore.AI.Chat.csproj (dns block)

If you need me to access, download, or install something from one of these locations, you can either:


var pager = new Pager(pagerParameters, pagerOptions.Value.GetPageSize());

var instances = (await _instanceManager.GetForCurrentUserAsync()).ToList();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't add .ToList()

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1141918 - removed .ToList() and now using .Count() method since GetForCurrentUserAsync() returns IEnumerable.


foreach (var entry in recentHistory)
{
var role = entry.Role?.ToLowerInvariant() == "user" ? ChatRole.User : ChatRole.Assistant;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we lower the role for comparison. I believe we already store it as ChatRole.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1141918 - now using ChatRole directly from the ChatMessageEntry without string conversion.

/// </summary>
public sealed class ChatMessageEntry
{
public string Role { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we store ChatRole instance instead of string?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1141918 - changed ChatMessageEntry.Role to use ChatRole type instead of string.

Comment on lines 35 to 36
int startIndex = 0;
int endIndex = startIndex + Model.Models.Count - 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
int startIndex = 0;
int endIndex = startIndex + Model.Models.Count - 1;
var startIndex = 0;
var endIndex = startIndex + Model.Models.Count - 1;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1141918 - now using var for the variable declarations.


namespace CrestApps.OrchardCore.AI.Chat;

public static class AICustomChatPermissions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move this in AICustomChatConstants so iot can be accessed AICustomChatConstants.Permissions.ManageOwnCustomChatInstances

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1141918 - moved the permission to AICustomChatConstants.Permissions.ManageOwnCustomChatInstances.

…e permission to Constants

- Remove .ToList() from controller line 71
- Change ChatMessageEntry.Role to use ChatRole instead of string
- Simplify AIChatHub to use ChatRole directly without string conversion
- Use var instead of int for variable declarations in Index.cshtml
- Move permission definition to AICustomChatConstants.Permissions class

Co-authored-by: MikeAlhayek <[email protected]>
@github-actions
Copy link

github-actions bot commented Jan 8, 2026

This pull request has merge conflicts. Please resolve those before requesting a review.

@MikeAlhayek MikeAlhayek closed this Jan 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants