Skip to content

Commit 0079adf

Browse files
committed
Add way to create new chat or chat with existing settings
1 parent c2fa802 commit 0079adf

3 files changed

Lines changed: 116 additions & 3 deletions

File tree

src/Modules/CrestApps.OrchardCore.AI.Chat.Interactions/Controllers/AdminController.cs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Security.Claims;
2+
using System.Text.RegularExpressions;
23
using CrestApps.OrchardCore.AI.Chat.Interactions.ViewModels;
34
using CrestApps.OrchardCore.AI.Core;
45
using CrestApps.OrchardCore.AI.Models;
@@ -244,6 +245,63 @@ public async Task<ActionResult> Chat(string source, string itemId)
244245
return View(model);
245246
}
246247

248+
[Admin("ai/chat/interaction/new-chat/{source}", "NewInteractionsChat")]
249+
public async Task<ActionResult> New(string source)
250+
=> RedirectToAction(nameof(Chat), new { source });
251+
252+
[Admin("ai/chat/interaction/clone-chat/{itemId}", "CloneInteractionsChat")]
253+
public async Task<ActionResult> Clone(string itemId)
254+
{
255+
var interaction = await _interactionManager.FindByIdAsync(itemId);
256+
257+
if (interaction is null)
258+
{
259+
return NotFound();
260+
}
261+
262+
if (!await _authorizationService.AuthorizeAsync(User, AIPermissions.EditChatInteractions, interaction))
263+
{
264+
return Forbid();
265+
}
266+
267+
var clonedInteraction = await _interactionManager.NewAsync(interaction.Source, interaction.Properties);
268+
clonedInteraction.Title = GetNextTitle(interaction.Title);
269+
clonedInteraction.DeploymentId = interaction.DeploymentId;
270+
clonedInteraction.ConnectionName = interaction.ConnectionName;
271+
clonedInteraction.SystemMessage = interaction.SystemMessage;
272+
clonedInteraction.Temperature = interaction.Temperature;
273+
clonedInteraction.TopP = interaction.TopP;
274+
clonedInteraction.FrequencyPenalty = interaction.FrequencyPenalty;
275+
clonedInteraction.PresencePenalty = interaction.PresencePenalty;
276+
clonedInteraction.MaxTokens = interaction.MaxTokens;
277+
clonedInteraction.PastMessagesCount = interaction.PastMessagesCount;
278+
clonedInteraction.DocumentTopN = interaction.DocumentTopN;
279+
clonedInteraction.ToolNames = interaction.ToolNames.ToList();
280+
clonedInteraction.ToolInstanceIds = interaction.ToolInstanceIds.ToList();
281+
clonedInteraction.McpConnectionIds = interaction.McpConnectionIds.ToList();
282+
clonedInteraction.Documents = interaction.Documents.ToList();
283+
clonedInteraction.Prompts = []; // Start with no prompts in the cloned interaction.
284+
clonedInteraction.DocumentIndex = interaction.Documents.Count; // Set the document index based on the cloned documents.
285+
286+
if (!await _authorizationService.AuthorizeAsync(User, AIPermissions.EditChatInteractions, clonedInteraction))
287+
{
288+
return Forbid();
289+
}
290+
291+
// Save the interaction immediately so it can be used by the SignalR hub.
292+
await _interactionManager.CreateAsync(clonedInteraction);
293+
294+
var model = new EditChatInteractionEntryViewModel
295+
{
296+
ItemId = clonedInteraction.ItemId,
297+
Source = clonedInteraction.Source,
298+
DisplayName = clonedInteraction.Title,
299+
Editor = await _interactionDisplayManager.BuildEditorAsync(clonedInteraction, _updateModelAccessor.ModelUpdater, isNew: true),
300+
};
301+
302+
return View(nameof(Chat), model);
303+
}
304+
247305
[HttpPost]
248306
public async Task<IActionResult> Delete(string itemId)
249307
{
@@ -270,4 +328,28 @@ public async Task<IActionResult> Delete(string itemId)
270328

271329
return RedirectToAction(nameof(Index));
272330
}
331+
332+
private static readonly Regex PostfixRegex = new Regex(@"\s*\((\d+)\)$", RegexOptions.Compiled);
333+
334+
private static string GetNextTitle(string title)
335+
{
336+
if (string.IsNullOrWhiteSpace(title))
337+
{
338+
return "Untitled (1)";
339+
}
340+
341+
var match = PostfixRegex.Match(title);
342+
343+
if (match.Success)
344+
{
345+
// Extract number and increment
346+
var number = int.Parse(match.Groups[1].Value);
347+
var baseTitle = title.Substring(0, match.Index).TrimEnd();
348+
349+
return $"{baseTitle} ({number + 1})";
350+
}
351+
352+
// No postfix found → start at (1)
353+
return $"{title} (1)";
354+
}
273355
}

src/Modules/CrestApps.OrchardCore.AI.Chat.Interactions/Views/ChatInteractionHeader.cshtml

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,27 @@
2323
</div>
2424

2525
<div class="d-flex flex-wrap gap-2">
26-
<a class="btn btn-secondary" asp-action="Edit" asp-controller="Admin" asp-route-source="@Model.Interaction.Source" asp-area="@AIConstants.Feature.ChatInteractions">
27-
<i class="fa-solid fa-plus"></i> @T["New Chat: '{0}'", source.DisplayName]
28-
</a>
26+
27+
<div class="btn-group" title="Actions">
28+
<button type="button" class="btn btn-sm btn-secondary dropdown-toggle actions" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
29+
<span>@T["New Chat"]</span>
30+
</button>
31+
<ul class="actions-menu dropdown-menu dropdown-menu-end">
32+
33+
<li>
34+
<a class="dropdown-item" asp-action="New" asp-controller="Admin" asp-route-source="@Model.Interaction.Source" asp-area="@AIConstants.Feature.ChatInteractions">
35+
<i class="fa-solid fa-plus"></i> @T["Empty Chat"]
36+
</a>
37+
</li>
38+
39+
<li>
40+
<a class="dropdown-item" asp-action="Clone" asp-controller="Admin" asp-route-itemId="@Model.Interaction.ItemId" asp-area="@AIConstants.Feature.ChatInteractions">
41+
<i class="fa-solid fa-plus"></i> @T["Chat with Preset Settings"]
42+
</a>
43+
</li>
44+
45+
</ul>
46+
</div>
2947

3048
<a class="btn btn-primary" asp-action="Index" asp-controller="Admin" asp-area="@AIConstants.Feature.ChatInteractions">
3149
<i class="fa-solid fa-list"></i> @T["Chat Interactions"]

src/Modules/CrestApps.OrchardCore.AI.Chat.Interactions/Views/Items/ChatInteraction.ActionsMenu.SummaryAdmin.cshtml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
1+
@using CrestApps.OrchardCore.AI.Core
12
@using CrestApps.OrchardCore.AI.Models
23
@using OrchardCore.DisplayManagement.Views
34

45
@model ShapeViewModel<ChatInteraction>
56

7+
<li>
8+
<a class="dropdown-item" asp-action="New" asp-controller="Admin" asp-route-source="@Model.Value.Source" asp-area="@AIConstants.Feature.ChatInteractions">
9+
<i class="fa-solid fa-plus"></i> @T["Empty Chat"]
10+
</a>
11+
</li>
12+
13+
<li>
14+
<a class="dropdown-item" asp-action="Clone" asp-controller="Admin" asp-route-itemId="@Model.Value.ItemId" asp-area="@AIConstants.Feature.ChatInteractions">
15+
<i class="fa-solid fa-plus"></i> @T["Chat with Preset Settings"]
16+
</a>
17+
</li>
18+
619
<li>
720
<a asp-action="Delete"
821
asp-route-itemId="@Model.Value.ItemId"

0 commit comments

Comments
 (0)