-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement database persistence for chat history #596
Comments
Additional Context: Tool Integration and PersistenceThe CLI tool integration from #595 needs to be considered in the database schema and persistence layer. We should extend the schema to handle tool execution: -- Add tool_executions table
CREATE TABLE tool_executions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
message_id UUID NOT NULL REFERENCES messages(id),
tool_name TEXT NOT NULL,
input JSONB NOT NULL,
output JSONB,
status TEXT NOT NULL, -- 'pending', 'success', 'error'
error_message TEXT,
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
completed_at TIMESTAMPTZ,
execution_time_ms INTEGER -- For performance tracking
);
-- Add indexes
CREATE INDEX tool_executions_message_id_idx ON tool_executions(message_id);
CREATE INDEX tool_executions_tool_name_idx ON tool_executions(tool_name);
CREATE INDEX tool_executions_status_idx ON tool_executions(status); This allows us to:
The CLI tool functionality (repo cloning, testing, analysis) will be tracked here, enabling:
We should also consider adding a (Comment from OpenAgents) |
Initial implementation progress:
Next steps:
The current implementation provides a solid foundation for both chat history persistence and tool execution tracking (via the tool_calls JSONB field in messages). (Comment from OpenAgents) |
Added comprehensive integration tests in tests/chat_database.rs:
The tests follow the pattern established in tests/user.rs with thorough assertions and cleanup. Next steps:
Would you like me to proceed with implementing the chat handlers next? (Comment from OpenAgents) |
WebSocket Integration PlanAfter reviewing the WebSocket implementation, here's how we'll integrate chat persistence without adding HTTP endpoints: 1. Extend WebSocket Message Types (src/server/ws/types.rs)pub enum ChatMessage {
// Existing
UserMessage { content: String },
AgentResponse { content: String },
Error { message: String },
// New conversation management messages
CreateConversation { user_id: String, title: Option<String> },
ListConversations { user_id: String },
SelectConversation { conversation_id: Uuid },
DeleteConversation { conversation_id: Uuid },
} 2. Enhance ChatHandler State (src/server/ws/handlers/chat.rs)pub struct ChatHandler {
ws_state: Arc<WebSocketState>,
github_service: Arc<GitHubService>,
chat_db: Arc<ChatDatabase>, // Add database service
active_conversations: DashMap<String, Uuid>, // Track active conversation per connection
} 3. Message FlowConnection Setup
Conversation Creation
Message Processing
Conversation Management
4. Client Message Examples// Create conversation
{
"type": "chat",
"type": "create_conversation",
"user_id": "user123",
"title": "New Chat"
}
// List conversations
{
"type": "chat",
"type": "list_conversations",
"user_id": "user123"
}
// Select conversation
{
"type": "chat",
"type": "select_conversation",
"conversation_id": "uuid-here"
}
// Regular chat message (now stored in DB)
{
"type": "chat",
"type": "user_message",
"content": "Hello!"
} 5. Server Response Examples// Conversation created
{
"type": "chat",
"status": "conversation_created",
"conversation": {
"id": "uuid",
"title": "New Chat",
"created_at": "timestamp"
}
}
// Conversation list
{
"type": "chat",
"status": "conversations_list",
"conversations": [
{
"id": "uuid",
"title": "Chat 1",
"updated_at": "timestamp"
}
]
}
// Chat response (now includes conversation context)
{
"type": "chat",
"status": "streaming",
"conversation_id": "uuid",
"content": "Hello! How can I help?",
"sender": "ai"
} Implementation Order
This approach:
Next step: Implement the message type changes and ChatHandler updates? (Comment from OpenAgents) |
Revised WebSocket Auth & Persistence PlanWebSocket Authentication
// In src/server/ws/mod.rs
pub async fn ws_handler(
ws: WebSocketUpgrade,
cookies: CookieJar,
State(state): State<Arc<AppState>>,
) -> impl IntoResponse {
// Get session from cookie
let session = cookies
.get(SESSION_COOKIE_NAME)
.and_then(|cookie| Session::validate(cookie.value(), &state.db_pool).await)
.ok_or(StatusCode::UNAUTHORIZED)?;
// Get user from session
let user = sqlx::query_as!(
User,
"SELECT * FROM users WHERE id = $1",
session.user_id
)
.fetch_one(&state.db_pool)
.await
.map_err(|_| StatusCode::UNAUTHORIZED)?;
// Store scramble_id in connection state
let conn_id = Uuid::new_v4();
state.ws_state.connections.insert(
conn_id.to_string(),
ConnectionState {
scramble_id: user.scramble_id.clone(),
// ... other fields
}
);
// Upgrade connection
ws.on_upgrade(|socket| handle_socket(socket, conn_id, state))
} ChatHandler Revisionpub struct ChatHandler {
ws_state: Arc<WebSocketState>,
github_service: Arc<GitHubService>,
chat_db: Arc<ChatDatabase>,
active_conversations: DashMap<String, Uuid>, // conn_id -> conversation_id
}
impl ChatHandler {
async fn get_user_from_conn(&self, conn_id: &str) -> Result<String> {
// Get scramble_id from connection state
self.ws_state
.connections
.get(conn_id)
.map(|conn| conn.scramble_id.clone())
.ok_or_else(|| anyhow!("No authenticated user for connection"))
}
async fn process_message(&self, content: String, conn_id: &str) -> Result<()> {
// Get user's scramble_id from connection state
let scramble_id = self.get_user_from_conn(conn_id).await?;
// Get or create active conversation
let conversation_id = match self.active_conversations.get(conn_id) {
Some(id) => *id,
None => {
// Create new conversation
let conv = self.chat_db
.create_conversation(CreateConversationRequest {
scramble_id,
title: None,
})
.await?;
self.active_conversations.insert(conn_id.to_string(), conv.id);
conv.id
}
};
// Store message
self.chat_db
.add_message(CreateMessageRequest {
conversation_id,
role: "user".to_string(),
content: content.clone(),
metadata: None,
tool_calls: None,
})
.await?;
// Process with model (existing code)
// ...
Ok(())
}
} Key Changes
Message Flow
This approach:
Next steps:
Thoughts on this revised approach? (Comment from OpenAgents) |
WebSocket Auth & Persistence PlanWebSocket Authentication & State
// In ConnectionState
pub struct ConnectionState {
user_id: i32, // From users.id
// ... other fields
}
// In WebSocket upgrade handler
pub async fn ws_handler(
ws: WebSocketUpgrade,
cookies: CookieJar,
State(state): State<Arc<AppState>>,
) -> impl IntoResponse {
// Get session from cookie
let session = cookies
.get(SESSION_COOKIE_NAME)
.and_then(|cookie| Session::validate(cookie.value(), &state.db_pool).await)
.ok_or(StatusCode::UNAUTHORIZED)?;
// Get user from session
let user = sqlx::query_as!(
User,
"SELECT * FROM users WHERE id = $1",
session.user_id
)
.fetch_one(&state.db_pool)
.await
.map_err(|_| StatusCode::UNAUTHORIZED)?;
// Store user.id in connection state
let conn_id = Uuid::new_v4();
state.ws_state.connections.insert(
conn_id.to_string(),
ConnectionState {
user_id: user.id,
// ... other fields
}
);
// Upgrade connection
ws.on_upgrade(|socket| handle_socket(socket, conn_id, state))
} ChatHandler Revisionpub struct ChatHandler {
ws_state: Arc<WebSocketState>,
github_service: Arc<GitHubService>,
chat_db: Arc<ChatDatabase>,
active_conversations: DashMap<String, Uuid>, // conn_id -> conversation_id
}
impl ChatHandler {
async fn get_user_id_from_conn(&self, conn_id: &str) -> Result<i32> {
// Get user.id from connection state
self.ws_state
.connections
.get(conn_id)
.map(|conn| conn.user_id)
.ok_or_else(|| anyhow!("No authenticated user for connection"))
}
async fn process_message(&self, content: String, conn_id: &str) -> Result<()> {
// Get user.id from connection state
let user_id = self.get_user_id_from_conn(conn_id).await?;
// Get or create active conversation
let conversation_id = match self.active_conversations.get(conn_id) {
Some(id) => *id,
None => {
// Create new conversation
let conv = self.chat_db
.create_conversation(CreateConversationRequest {
user_id,
title: None,
})
.await?;
self.active_conversations.insert(conn_id.to_string(), conv.id);
conv.id
}
};
// Store message
self.chat_db
.add_message(CreateMessageRequest {
conversation_id,
role: "user".to_string(),
content: content.clone(),
metadata: None,
tool_calls: None,
})
.await?;
// Process with model (existing code)
// ...
Ok(())
}
} Key Points
Message Flow
Next steps:
Would you like me to start with implementing the WebSocket connection handler changes? (Comment from OpenAgents) |
Database Persistence for Chat History
Overview
Implement proper database-based persistence for chat history and conversations, replacing the proposed in-memory approach from #589.
Required Changes
1. Database Schema
2. Database Service
3. Chat Handler Integration
4. API Endpoints
5. UI Updates
Implementation Steps
Success Criteria
Related Issues
The text was updated successfully, but these errors were encountered: