Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/RockBot.Agent/ClearContextHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using Microsoft.Extensions.Logging;
using RockBot.Host;
using RockBot.Messaging;
using RockBot.UserProxy;

namespace RockBot.Agent;

/// <summary>
/// Handles <see cref="ClearContextRequest"/> by clearing conversation memory
/// for the session. Long-term memory and conversation logs are preserved.
/// </summary>
internal sealed class ClearContextHandler(
IConversationMemory conversationMemory,
ISessionTracker sessionTracker,
IMessagePublisher publisher,
AgentIdentity agent,
ILogger<ClearContextHandler> logger) : IMessageHandler<ClearContextRequest>
{
public async Task HandleAsync(ClearContextRequest message, MessageHandlerContext context)
{
var ct = context.CancellationToken;

// Cancel any in-flight work for this session
var handle = sessionTracker.BeginSession(message.SessionId, ct);
sessionTracker.EndSession(message.SessionId, handle.Generation);

// Clear conversation memory (ephemeral turns only — logs are preserved)
await conversationMemory.ClearAsync(message.SessionId, ct);

logger.LogInformation("Cleared conversation context for session {SessionId}", message.SessionId);

var reply = new AgentReply
{
Content = "Context cleared — starting fresh.",
SessionId = message.SessionId,
AgentName = agent.Name,
IsFinal = true
};
var envelope = reply.ToEnvelope<AgentReply>(source: agent.Name);
await publisher.PublishAsync(UserProxyTopics.UserResponse, envelope, ct);
}
}
2 changes: 2 additions & 0 deletions src/RockBot.Agent/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ IChatClient BuildClient(LlmTierConfig config)
agent.HandleMessage<UserMessage, UserMessageHandler>();
agent.HandleMessage<UserFeedback, UserFeedbackHandler>();
agent.HandleMessage<CancelSessionRequest, CancelSessionHandler>();
agent.HandleMessage<ClearContextRequest, ClearContextHandler>();
agent.HandleMessage<ConversationHistoryRequest, ConversationHistoryRequestHandler>();
agent.HandleMessage<AgentInfoRequest, AgentInfoRequestHandler>();
agent.WithSavedResponses();
Expand All @@ -237,6 +238,7 @@ IChatClient BuildClient(LlmTierConfig config)
agent.SubscribeTo(UserProxyTopics.UserMessage);
agent.SubscribeTo(UserProxyTopics.UserFeedback);
agent.SubscribeTo(UserProxyTopics.CancelSession);
agent.SubscribeTo(UserProxyTopics.ClearContext);
agent.SubscribeTo(UserProxyTopics.ConversationHistoryRequest);
agent.SubscribeTo(UserProxyTopics.AgentInfoRequest);
agent.SubscribeTo(UserProxyTopics.SaveResponseRequest);
Expand Down
10 changes: 10 additions & 0 deletions src/RockBot.UserProxy.Abstractions/ClearContextRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace RockBot.UserProxy;

/// <summary>
/// Message sent from the user proxy to clear conversation context for a session.
/// Long-term memory and conversation logs are preserved.
/// </summary>
public sealed record ClearContextRequest
{
public required string SessionId { get; init; }
}
1 change: 1 addition & 0 deletions src/RockBot.UserProxy.Abstractions/UserProxyTopics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public static class UserProxyTopics
public const string UserMessage = "user.message";
public const string UserResponse = "user.response";
public const string CancelSession = "user.cancel";
public const string ClearContext = "user.context.clear";
public const string ConversationHistoryRequest = "user.history.request";
public const string ConversationHistoryResponse = "user.history.response";
public const string UserFeedback = "user.feedback";
Expand Down
15 changes: 15 additions & 0 deletions src/RockBot.UserProxy.Blazor/Pages/Chat.razor
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,21 @@
currentMessage = string.Empty;
await JSRuntime.InvokeVoidAsync("chatHelpers.addToHistory", messageInput, messageContent);

// Handle /clear command — pure plumbing, no LLM involved
if (messageContent.Equals("/clear", StringComparison.OrdinalIgnoreCase))
{
ChatState.ClearMessages();
try
{
await ProxyService.ClearContextAsync(SessionId);
}
catch (Exception ex)
{
ChatState.AddError($"Could not clear agent context: {ex.Message}");
}
return;
}

ChatState.AddUserMessage(messageContent, UserId, SessionId);
ChatState.SetProcessing(true);

Expand Down
14 changes: 14 additions & 0 deletions src/RockBot.UserProxy.Blazor/Services/ChatStateService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,20 @@ public void AddError(string message)
NotifyStateChanged();
}

// ── Clear context ───────────────────────────────────────────────────────

public void ClearMessages()
{
lock (_lock)
{
_messages.Clear();
_activeActivityLogs.Clear();
}
_currentThinkingMessage = null;
_isProcessing = false;
NotifyStateChanged();
}

// ── Saved references ────────────────────────────────────────────────────

public void AddSavedReference(string id, string label, string content, string agentName)
Expand Down
12 changes: 12 additions & 0 deletions src/RockBot.UserProxy/UserProxyService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,18 @@ public async Task CancelSessionAsync(string sessionId, CancellationToken cancell
logger.LogInformation("Published cancel request for session {SessionId}", sessionId);
}

/// <summary>
/// Publishes a clear context request to reset conversation memory for the given session.
/// Long-term memory and conversation logs are preserved.
/// </summary>
public async Task ClearContextAsync(string sessionId, CancellationToken cancellationToken = default)
{
var request = new ClearContextRequest { SessionId = sessionId };
var envelope = request.ToEnvelope<ClearContextRequest>(source: options.ProxyId);
await publisher.PublishAsync(UserProxyTopics.ClearContext, envelope, cancellationToken);
logger.LogInformation("Published clear context request for session {SessionId}", sessionId);
}

internal Task<MessageResult> HandleResponseAsync(MessageEnvelope envelope, CancellationToken ct)
{
AgentReply? reply;
Expand Down
Loading