fix: persist state in redis

This commit is contained in:
Shivam Mishra
2025-07-08 20:18:44 +05:30
parent c349f5c6f9
commit 61ab06a6b1
2 changed files with 72 additions and 17 deletions
@@ -44,6 +44,9 @@ module Captain
# Build context for the agent
context = build_context
# Load persisted state from Redis
context = load_persisted_state(context)
# Create a runner and execute
runner = ::Agents::Runner.with_agents(agent)
@@ -52,6 +55,9 @@ module Captain
result = runner.run(@message_content, context: context)
# Persist updated state to Redis
persist_state(result.context) if result.respond_to?(:context) && result.context
response_content = result.output
# Generate and save the response
@@ -73,6 +79,7 @@ module Captain
def build_context
context = {
conversation_history: build_conversation_history,
state: {
user_info: {
id: @user.id,
@@ -87,21 +94,7 @@ module Captain
domain: @account.domain,
feature_flags: @account.feature_flags
},
conversation_history: [
{
content: @message_content,
role: 'user'
}
],
current_time: Time.current.iso8601,
available_capabilities: %w[
conversation_management
contact_research
knowledge_base_search
team_information
workflow_automation
sentiment_analysis
]
current_time: Time.current.iso8601
}
}
@@ -124,13 +117,39 @@ module Captain
context
end
def build_conversation_history
history = []
# Get all previous messages from the thread (excluding current message)
previous_messages = @copilot_thread.copilot_messages
.where.not(message_type: 'assistant_thinking')
.order(:created_at)
# this will also contain the current message
previous_messages.each do |message|
role = case message.message_type
when 'user' then 'user'
when 'assistant' then 'assistant'
else next # Skip unknown message types
end
history << {
content: message.message['content'],
role: role
}
end
history
end
def setup_callbacks(runner)
runner.on_agent_thinking do |agent_name, _input|
broadcast_thinking_message("#{agent_name} is thinking...")
end
runner.on_tool_start do |tool_name, _args|
broadcast_thinking_message("Using #{tool_name}...")
runner.on_tool_start do |tool_name, args|
args_display = args.present? ? " with #{args.to_json}" : ''
broadcast_thinking_message("Using #{tool_name}#{args_display}...")
end
runner.on_tool_complete do |tool_name, _result|
@@ -142,6 +161,36 @@ module Captain
end
end
def load_persisted_state(context)
redis_key = "copilot_state:#{@copilot_thread.id}"
begin
persisted_state = Redis::Alfred.get(redis_key)
if persisted_state
# Merge persisted state with current context
context[:state] = context[:state].merge(persisted_state.with_indifferent_access)
end
rescue StandardError => e
Rails.logger.warn "Failed to load persisted state from Redis: #{e.message}"
end
context
end
def persist_state(context)
redis_key = "copilot_state:#{@copilot_thread.id}"
begin
# Extract only the state we want to persist (excluding conversation_history)
state_to_persist = context[:state]&.except(:conversation_history, :current_time) || {}
# Set expiration to 24 hours
Redis::Alfred.set(redis_key, state_to_persist, expire: 24.hours.to_i)
rescue StandardError => e
Rails.logger.warn "Failed to persist state to Redis: #{e.message}"
end
end
def broadcast_thinking_message(content)
message_data = { content: content }
@@ -51,6 +51,12 @@ class Captain::Agents::CopilotOrchestratorAgent
- For status updates: integrations_agent("Mark conversation as resolved")
- For help content: knowledge_agent("Find billing policy")
IMPORTANT CONTEXT RULES:
1. Always maintain conversation context between messages
2. If user asks about "that conversation" or "the message", refer to the most recently accessed conversation
3. Keep track of previously accessed conversations in the current session
4. Don't re-fetch data that was already retrieved in the current conversation thread
Always get conversation data first before analysis or recommendations.
INSTRUCTIONS