245 lines
7.7 KiB
Rust
245 lines
7.7 KiB
Rust
use rusqlite::{params, Connection, Result};
|
|
use serde::{Deserialize, Serialize};
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct LiveSession {
|
|
pub id: String,
|
|
pub project_id: String,
|
|
pub agent_id: String,
|
|
pub title: String,
|
|
pub status: String,
|
|
pub created_at: String,
|
|
pub updated_at: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct LiveMessage {
|
|
pub id: String,
|
|
pub session_id: String,
|
|
pub sender: String,
|
|
pub content: String,
|
|
pub created_at: String,
|
|
}
|
|
|
|
fn session_from_row(row: &rusqlite::Row) -> rusqlite::Result<LiveSession> {
|
|
Ok(LiveSession {
|
|
id: row.get(0)?,
|
|
project_id: row.get(1)?,
|
|
agent_id: row.get(2)?,
|
|
title: row.get(3)?,
|
|
status: row.get(4)?,
|
|
created_at: row.get(5)?,
|
|
updated_at: row.get(6)?,
|
|
})
|
|
}
|
|
|
|
fn message_from_row(row: &rusqlite::Row) -> rusqlite::Result<LiveMessage> {
|
|
Ok(LiveMessage {
|
|
id: row.get(0)?,
|
|
session_id: row.get(1)?,
|
|
sender: row.get(2)?,
|
|
content: row.get(3)?,
|
|
created_at: row.get(4)?,
|
|
})
|
|
}
|
|
|
|
impl LiveSession {
|
|
pub fn create(
|
|
conn: &Connection,
|
|
project_id: &str,
|
|
agent_id: &str,
|
|
title: &str,
|
|
) -> Result<LiveSession> {
|
|
let id = Uuid::new_v4().to_string();
|
|
let now = chrono::Utc::now().to_rfc3339();
|
|
|
|
conn.execute(
|
|
"INSERT INTO project_live_sessions (id, project_id, agent_id, title, status, created_at, updated_at)
|
|
VALUES (?1, ?2, ?3, ?4, 'active', ?5, ?6)",
|
|
params![id, project_id, agent_id, title, now, now],
|
|
)?;
|
|
|
|
Ok(LiveSession {
|
|
id,
|
|
project_id: project_id.to_string(),
|
|
agent_id: agent_id.to_string(),
|
|
title: title.to_string(),
|
|
status: "active".to_string(),
|
|
created_at: now.clone(),
|
|
updated_at: now,
|
|
})
|
|
}
|
|
|
|
pub fn list_by_project(conn: &Connection, project_id: &str) -> Result<Vec<LiveSession>> {
|
|
let mut stmt = conn.prepare(
|
|
"SELECT id, project_id, agent_id, title, status, created_at, updated_at
|
|
FROM project_live_sessions
|
|
WHERE project_id = ?1
|
|
ORDER BY updated_at DESC",
|
|
)?;
|
|
let rows = stmt.query_map(params![project_id], session_from_row)?;
|
|
rows.collect()
|
|
}
|
|
|
|
pub fn get_by_id(conn: &Connection, id: &str) -> Result<LiveSession> {
|
|
conn.query_row(
|
|
"SELECT id, project_id, agent_id, title, status, created_at, updated_at
|
|
FROM project_live_sessions
|
|
WHERE id = ?1",
|
|
params![id],
|
|
session_from_row,
|
|
)
|
|
}
|
|
|
|
pub fn touch(conn: &Connection, id: &str) -> Result<()> {
|
|
let now = chrono::Utc::now().to_rfc3339();
|
|
conn.execute(
|
|
"UPDATE project_live_sessions SET updated_at = ?1 WHERE id = ?2",
|
|
params![now, id],
|
|
)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl LiveMessage {
|
|
pub fn insert(
|
|
conn: &Connection,
|
|
session_id: &str,
|
|
sender: &str,
|
|
content: &str,
|
|
) -> Result<LiveMessage> {
|
|
let id = Uuid::new_v4().to_string();
|
|
let now = chrono::Utc::now().to_rfc3339();
|
|
|
|
conn.execute(
|
|
"INSERT INTO project_live_messages (id, session_id, sender, content, created_at)
|
|
VALUES (?1, ?2, ?3, ?4, ?5)",
|
|
params![id, session_id, sender, content, now],
|
|
)?;
|
|
|
|
LiveSession::touch(conn, session_id)?;
|
|
|
|
Ok(LiveMessage {
|
|
id,
|
|
session_id: session_id.to_string(),
|
|
sender: sender.to_string(),
|
|
content: content.to_string(),
|
|
created_at: now,
|
|
})
|
|
}
|
|
|
|
pub fn list_by_session(conn: &Connection, session_id: &str) -> Result<Vec<LiveMessage>> {
|
|
let mut stmt = conn.prepare(
|
|
"SELECT id, session_id, sender, content, created_at
|
|
FROM project_live_messages
|
|
WHERE session_id = ?1
|
|
ORDER BY created_at ASC",
|
|
)?;
|
|
let rows = stmt.query_map(params![session_id], message_from_row)?;
|
|
rows.collect()
|
|
}
|
|
|
|
pub fn list_recent_by_session(
|
|
conn: &Connection,
|
|
session_id: &str,
|
|
limit: usize,
|
|
) -> Result<Vec<LiveMessage>> {
|
|
let mut stmt = conn.prepare(
|
|
"SELECT id, session_id, sender, content, created_at
|
|
FROM project_live_messages
|
|
WHERE session_id = ?1
|
|
ORDER BY created_at DESC
|
|
LIMIT ?2",
|
|
)?;
|
|
let mut messages: Vec<LiveMessage> = stmt
|
|
.query_map(params![session_id, limit as i64], message_from_row)?
|
|
.collect::<Result<Vec<_>>>()?;
|
|
messages.reverse();
|
|
Ok(messages)
|
|
}
|
|
|
|
pub fn update_content(conn: &Connection, id: &str, content: &str) -> Result<()> {
|
|
conn.execute(
|
|
"UPDATE project_live_messages SET content = ?1 WHERE id = ?2",
|
|
params![content, id],
|
|
)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn delete(conn: &Connection, id: &str) -> Result<()> {
|
|
conn.execute(
|
|
"DELETE FROM project_live_messages WHERE id = ?1",
|
|
params![id],
|
|
)?;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::db;
|
|
use crate::models::agent::{Agent, AgentRole, AgentTool};
|
|
use crate::models::project::Project;
|
|
|
|
fn setup() -> (Connection, String, String) {
|
|
let conn = db::init_in_memory().expect("db init should succeed");
|
|
let project = Project::insert(&conn, "Live Project", "/tmp/live-project", None, "main")
|
|
.expect("project insert should succeed");
|
|
let agent = Agent::insert(
|
|
&conn,
|
|
"Live Agent",
|
|
AgentRole::Analyst,
|
|
AgentTool::Codex,
|
|
"",
|
|
)
|
|
.expect("agent insert should succeed");
|
|
|
|
(conn, project.id, agent.id)
|
|
}
|
|
|
|
#[test]
|
|
fn test_create_session_and_list_messages() {
|
|
let (conn, project_id, agent_id) = setup();
|
|
let session = LiveSession::create(&conn, &project_id, &agent_id, "Session 1")
|
|
.expect("session create");
|
|
|
|
let user_message =
|
|
LiveMessage::insert(&conn, &session.id, "user", "Bonjour").expect("message insert");
|
|
let agent_message =
|
|
LiveMessage::insert(&conn, &session.id, "agent", "Salut").expect("message insert");
|
|
|
|
let sessions =
|
|
LiveSession::list_by_project(&conn, &project_id).expect("session list should work");
|
|
let messages =
|
|
LiveMessage::list_by_session(&conn, &session.id).expect("message list should work");
|
|
|
|
assert_eq!(sessions.len(), 1);
|
|
assert_eq!(messages.len(), 2);
|
|
assert_eq!(messages[0].id, user_message.id);
|
|
assert_eq!(messages[1].id, agent_message.id);
|
|
}
|
|
|
|
#[test]
|
|
fn test_update_and_delete_message() {
|
|
let (conn, project_id, agent_id) = setup();
|
|
let session = LiveSession::create(&conn, &project_id, &agent_id, "Session 2")
|
|
.expect("session create");
|
|
let message = LiveMessage::insert(&conn, &session.id, "agent", "")
|
|
.expect("message insert should work");
|
|
|
|
LiveMessage::update_content(&conn, &message.id, "Streaming done")
|
|
.expect("message update should work");
|
|
|
|
let messages =
|
|
LiveMessage::list_by_session(&conn, &session.id).expect("message list should work");
|
|
assert_eq!(messages.len(), 1);
|
|
assert_eq!(messages[0].content, "Streaming done");
|
|
|
|
LiveMessage::delete(&conn, &message.id).expect("message delete should work");
|
|
let messages_after_delete =
|
|
LiveMessage::list_by_session(&conn, &session.id).expect("message list should work");
|
|
assert!(messages_after_delete.is_empty());
|
|
}
|
|
}
|