diff --git a/lumni/src/apps/builtin/llm/prompt/src/chat/db/display.rs b/lumni/src/apps/builtin/llm/prompt/src/chat/db/display.rs index 45f0db14..73571ca0 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/chat/db/display.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/chat/db/display.rs @@ -1,8 +1,6 @@ use lumni::api::error::ApplicationError; -use super::schema::{ - Conversation, ConversationId, Message, -}; +use super::schema::{Conversation, ConversationId, Message}; use super::ConversationDatabaseStore; pub use crate::external as lumni; @@ -39,9 +37,10 @@ impl ConversationDatabaseStore { id: &str, ) -> Result<(), ApplicationError> { let conversation_id = ConversationId(id.parse().map_err(|_| { - ApplicationError::NotFound( - format!("Conversation {id} not found in database"), - )})?); + ApplicationError::NotFound(format!( + "Conversation {id} not found in database" + )) + })?); if let Some((conversation, messages)) = self.fetch_conversation(Some(conversation_id), None)? diff --git a/lumni/src/apps/builtin/llm/prompt/src/chat/db/mod.rs b/lumni/src/apps/builtin/llm/prompt/src/chat/db/mod.rs index 211eab1b..8b53a12b 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/chat/db/mod.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/chat/db/mod.rs @@ -3,7 +3,9 @@ mod display; mod schema; mod store; -pub use schema::{ConversationCache, Exchange, ExchangeId, ConversationId, Message, ModelId}; +pub use schema::{ + ConversationCache, ConversationId, Exchange, ExchangeId, Message, ModelId, +}; pub use store::ConversationDatabaseStore; pub use super::PromptRole; diff --git a/lumni/src/apps/builtin/llm/prompt/src/chat/db/schema.rs b/lumni/src/apps/builtin/llm/prompt/src/chat/db/schema.rs index 847bce12..7a982c43 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/chat/db/schema.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/chat/db/schema.rs @@ -31,8 +31,11 @@ pub struct Conversation { pub id: ConversationId, pub name: String, pub metadata: serde_json::Value, + pub model_id: ModelId, pub parent_conversation_id: Option, pub fork_exchange_id: Option, + pub completion_options: Option, + pub prompt_options: Option, pub schema_version: i64, pub created_at: i64, pub updated_at: i64, @@ -43,10 +46,6 @@ pub struct Conversation { pub struct Exchange { pub id: ExchangeId, pub conversation_id: ConversationId, - pub model_id: ModelId, - pub system_prompt: Option, - pub completion_options: Option, - pub prompt_options: Option, pub created_at: i64, pub previous_exchange_id: Option, pub is_deleted: bool, @@ -223,4 +222,14 @@ impl ConversationCache { }) .unwrap_or_default() } + + pub fn get_system_prompt(&self) -> Option { + // system prompt is always set in the first exchange + self.get_exchanges().first().and_then(|exchange| { + self.get_exchange_messages(exchange.id) + .iter() + .find(|message| message.role == PromptRole::System) + .map(|message| message.content.clone()) + }) + } } diff --git a/lumni/src/apps/builtin/llm/prompt/src/chat/db/schema.sql b/lumni/src/apps/builtin/llm/prompt/src/chat/db/schema.sql index 649f5d27..78fe67bb 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/chat/db/schema.sql +++ b/lumni/src/apps/builtin/llm/prompt/src/chat/db/schema.sql @@ -16,8 +16,11 @@ CREATE TABLE conversations ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, metadata TEXT, -- JSON string including description and other metadata + model_id INTEGER NOT NULL, parent_conversation_id INTEGER, fork_exchange_id INTEGER, + completion_options TEXT, -- JSON string + prompt_options TEXT, -- JSON string schema_version INTEGER NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, @@ -26,21 +29,17 @@ CREATE TABLE conversations ( is_deleted BOOLEAN DEFAULT FALSE, FOREIGN KEY (parent_conversation_id) REFERENCES conversations(id), FOREIGN KEY (fork_exchange_id) REFERENCES exchanges(id) + FOREIGN KEY (model_id) REFERENCES models(model_id) ); CREATE TABLE exchanges ( id INTEGER PRIMARY KEY AUTOINCREMENT, conversation_id INTEGER NOT NULL, - model_id INTEGER NOT NULL, - system_prompt TEXT, - completion_options TEXT, -- JSON string - prompt_options TEXT, -- JSON string created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, previous_exchange_id INTEGER, is_deleted BOOLEAN DEFAULT FALSE, is_latest BOOLEAN DEFAULT TRUE, FOREIGN KEY (conversation_id) REFERENCES conversations(id), - FOREIGN KEY (model_id) REFERENCES models(model_id) FOREIGN KEY (previous_exchange_id) REFERENCES exchanges(id) ); @@ -83,7 +82,6 @@ CREATE INDEX idx_parent_conversation ON conversations(parent_conversation_id); CREATE INDEX idx_exchange_conversation_latest ON exchanges(conversation_id, is_latest); CREATE INDEX idx_exchange_created_at ON exchanges(created_at); CREATE INDEX idx_fork_exchange ON conversations(fork_exchange_id); -CREATE INDEX idx_model_id ON exchanges(model_id); +CREATE INDEX idx_conversation_model_id ON conversations(model_id); CREATE INDEX idx_conversation_created_at ON exchanges(conversation_id, created_at); CREATE INDEX idx_attachment_message ON attachments(message_id); - diff --git a/lumni/src/apps/builtin/llm/prompt/src/chat/db/store.rs b/lumni/src/apps/builtin/llm/prompt/src/chat/db/store.rs index 3570b3d2..4fc01e6e 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/chat/db/store.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/chat/db/store.rs @@ -6,7 +6,7 @@ use rusqlite::{params, Error as SqliteError, OptionalExtension}; use super::connector::DatabaseConnector; use super::schema::{ Attachment, AttachmentData, Conversation, ConversationCache, - ConversationId, Exchange, ExchangeId, Message, MessageId, + ConversationId, Exchange, ExchangeId, Message, MessageId, ModelId, }; pub struct ConversationDatabaseStore { @@ -24,13 +24,18 @@ impl ConversationDatabaseStore { &self, name: &str, parent_id: Option, + completion_options: Option, + prompt_options: Option, ) -> Result { let conversation = Conversation { id: ConversationId(-1), // Temporary ID name: name.to_string(), metadata: serde_json::Value::Null, + model_id: ModelId(0), parent_conversation_id: parent_id, fork_exchange_id: None, + completion_options, + prompt_options, schema_version: 1, created_at: 0, updated_at: 0, @@ -66,25 +71,44 @@ impl ConversationDatabaseStore { conversation: &Conversation, ) -> Result { let conversation_sql = format!( - "INSERT INTO conversations (name, metadata, \ - parent_conversation_id, fork_exchange_id, schema_version, \ - created_at, updated_at, is_deleted) - VALUES ('{}', {}, {}, {}, {}, {}, {}, {});", + "INSERT INTO conversations ( + name, metadata, model_id, parent_conversation_id, \ + fork_exchange_id, + completion_options, prompt_options, schema_version, + created_at, updated_at, is_deleted + ) + VALUES ('{}', {}, {}, {}, {}, {}, {}, {}, {}, {}, {});", conversation.name.replace("'", "''"), serde_json::to_string(&conversation.metadata) .map(|s| format!("'{}'", s.replace("'", "''"))) .unwrap_or_else(|_| "NULL".to_string()), + conversation.model_id.0, conversation .parent_conversation_id .map_or("NULL".to_string(), |id| id.0.to_string()), conversation .fork_exchange_id .map_or("NULL".to_string(), |id| id.0.to_string()), + conversation.completion_options.as_ref().map_or( + "NULL".to_string(), + |v| format!( + "'{}'", + serde_json::to_string(v).unwrap().replace("'", "''") + ) + ), + conversation.prompt_options.as_ref().map_or( + "NULL".to_string(), + |v| format!( + "'{}'", + serde_json::to_string(v).unwrap().replace("'", "''") + ) + ), conversation.schema_version, conversation.created_at, conversation.updated_at, conversation.is_deleted ); + let mut db = self.db.lock().unwrap(); db.queue_operation(conversation_sql); db.process_queue_with_result(|tx| { @@ -123,24 +147,10 @@ impl ConversationDatabaseStore { // Insert the exchange (without token-related fields) let exchange_sql = format!( - "INSERT INTO exchanges (conversation_id, model_id, system_prompt, - completion_options, prompt_options, created_at, previous_exchange_id, \ - is_deleted, is_latest) - VALUES ({}, {}, {}, {}, {}, {}, {}, {}, TRUE);", + "INSERT INTO exchanges (conversation_id, created_at, \ + previous_exchange_id, is_deleted, is_latest) + VALUES ({}, {}, {}, {}, TRUE);", exchange.conversation_id.0, - exchange.model_id.0, - exchange.system_prompt.as_ref().map_or( - "NULL".to_string(), - |s| format!("'{}'", s.replace("'", "''")) - ), - exchange.completion_options.as_ref().map_or( - "NULL".to_string(), - |v| format!("'{}'", v.to_string().replace("'", "''")) - ), - exchange.prompt_options.as_ref().map_or( - "NULL".to_string(), - |v| format!("'{}'", v.to_string().replace("'", "''")) - ), exchange.created_at, last_exchange_id.map_or("NULL".to_string(), |id| id.to_string()), exchange.is_deleted @@ -226,94 +236,6 @@ impl ConversationDatabaseStore { db.process_queue()?; Ok(()) } - - pub fn fetch_recent_conversations( - &self, - limit: usize, - ) -> Result>)>, SqliteError> { - let query = format!( - "SELECT c.*, m.id as message_id, m.role, m.message_type, \ - m.content, m.has_attachments, m.token_length, m.created_at as \ - message_created_at - FROM conversations c - LEFT JOIN exchanges e ON c.id = e.conversation_id AND e.is_latest \ - = TRUE - LEFT JOIN messages m ON e.id = m.exchange_id - WHERE c.is_deleted = FALSE - ORDER BY c.updated_at DESC, m.created_at ASC - LIMIT {}", - limit - ); - - let mut db = self.db.lock().unwrap(); - db.process_queue_with_result(|tx| { - let mut stmt = tx.prepare(&query)?; - let rows = stmt.query_map([], |row| { - let conversation = Conversation { - id: ConversationId(row.get(0)?), - name: row.get(1)?, - metadata: serde_json::from_str(&row.get::<_, String>(2)?) - .unwrap_or_default(), - parent_conversation_id: row.get(3).map(ConversationId).ok(), - fork_exchange_id: row.get(4).map(ExchangeId).ok(), - schema_version: row.get(5)?, - created_at: row.get(6)?, - updated_at: row.get(7)?, - is_deleted: row.get(10)?, - }; - - let message = if !row.get::<_, Option>(11)?.is_none() { - Some(Message { - id: MessageId(row.get(11)?), - conversation_id: conversation.id, - exchange_id: ExchangeId(row.get(0)?), // Using conversation_id as exchange_id - role: row.get(12)?, - message_type: row.get(13)?, - content: row.get(14)?, - has_attachments: row.get(15)?, - token_length: row.get(16)?, - created_at: row.get(17)?, - is_deleted: false, - }) - } else { - None - }; - - Ok((conversation, message)) - })?; - - let mut result = Vec::new(); - let mut current_conversation: Option = None; - let mut current_messages = Vec::new(); - - for row in rows { - let (conversation, message) = row?; - - if current_conversation - .as_ref() - .map_or(true, |c| c.id != conversation.id) - { - if let Some(conv) = current_conversation.take() { - result.push(( - conv, - Some(std::mem::take(&mut current_messages)), - )); - } - current_conversation = Some(conversation); - } - - if let Some(msg) = message { - current_messages.push(msg); - } - } - - if let Some(conv) = current_conversation.take() { - result.push((conv, Some(current_messages))); - } - - Ok(result) - }) - } } impl ConversationDatabaseStore { @@ -354,25 +276,32 @@ impl ConversationDatabaseStore { name: row.get(1)?, metadata: serde_json::from_str(&row.get::<_, String>(2)?) .unwrap_or_default(), - parent_conversation_id: row.get(3).map(ConversationId).ok(), - fork_exchange_id: row.get(4).map(ExchangeId).ok(), - schema_version: row.get(5)?, - created_at: row.get(6)?, - updated_at: row.get(7)?, - is_deleted: row.get(10)?, + model_id: ModelId(row.get(3)?), + parent_conversation_id: row.get(4).map(ConversationId).ok(), + fork_exchange_id: row.get(5).map(ExchangeId).ok(), + completion_options: row + .get::<_, Option>(6)? + .map(|s| serde_json::from_str(&s).unwrap_or_default()), + prompt_options: row + .get::<_, Option>(7)? + .map(|s| serde_json::from_str(&s).unwrap_or_default()), + schema_version: row.get(8)?, + created_at: row.get(9)?, + updated_at: row.get(10)?, + is_deleted: row.get(11)?, }; - let message = if !row.get::<_, Option>(11)?.is_none() { + let message = if !row.get::<_, Option>(14)?.is_none() { Some(Message { - id: MessageId(row.get(11)?), + id: MessageId(row.get(14)?), conversation_id: conversation.id, exchange_id: ExchangeId(row.get(0)?), - role: row.get(12)?, - message_type: row.get(13)?, - content: row.get(14)?, - has_attachments: row.get(15)?, - token_length: row.get(16)?, - created_at: row.get(17)?, + role: row.get(15)?, + message_type: row.get(16)?, + content: row.get(17)?, + has_attachments: row.get(18)?, + token_length: row.get(19)?, + created_at: row.get(20)?, is_deleted: false, }) } else { @@ -398,12 +327,16 @@ impl ConversationDatabaseStore { Ok(conversation.map(|c| (c, messages))) }) } + pub fn fetch_conversation_list( &self, limit: usize, ) -> Result, SqliteError> { let query = format!( - "SELECT id, name, updated_at + "SELECT id, name, metadata, model_id, parent_conversation_id, \ + fork_exchange_id, + completion_options, prompt_options, schema_version, + created_at, updated_at, is_deleted FROM conversations WHERE is_deleted = FALSE ORDER BY updated_at DESC @@ -418,14 +351,25 @@ impl ConversationDatabaseStore { Ok(Conversation { id: ConversationId(row.get(0)?), name: row.get(1)?, - updated_at: row.get(2)?, - // Set other fields to default values or None - metadata: serde_json::Value::Null, - parent_conversation_id: None, - fork_exchange_id: None, - schema_version: 1, - created_at: 0, - is_deleted: false, + metadata: serde_json::from_str(&row.get::<_, String>(2)?) + .unwrap_or_default(), + model_id: ModelId(row.get(3)?), + parent_conversation_id: row + .get::<_, Option>(4)? + .map(ConversationId), + fork_exchange_id: row + .get::<_, Option>(5)? + .map(ExchangeId), + completion_options: row + .get::<_, Option>(6)? + .map(|s| serde_json::from_str(&s).unwrap_or_default()), + prompt_options: row + .get::<_, Option>(7)? + .map(|s| serde_json::from_str(&s).unwrap_or_default()), + schema_version: row.get(8)?, + created_at: row.get(9)?, + updated_at: row.get(10)?, + is_deleted: row.get(11)?, }) })?; diff --git a/lumni/src/apps/builtin/llm/prompt/src/chat/instruction.rs b/lumni/src/apps/builtin/llm/prompt/src/chat/instruction.rs index e2680486..5f0e9243 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/chat/instruction.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/chat/instruction.rs @@ -1,9 +1,10 @@ use std::collections::HashMap; + use lumni::api::error::ApplicationError; use super::db::{ - ConversationCache, ConversationDatabaseStore, Exchange, ExchangeId, - Message, ModelId, ConversationId, + self, ConversationCache, ConversationDatabaseStore, ConversationId, + Exchange, ExchangeId, Message, }; use super::prompt::Prompt; use super::{ @@ -16,7 +17,6 @@ pub struct PromptInstruction { cache: ConversationCache, completion_options: ChatCompletionOptions, prompt_options: PromptOptions, - system_prompt: String, prompt_template: Option, } @@ -32,7 +32,6 @@ impl Default for PromptInstruction { cache: ConversationCache::new(), completion_options, prompt_options: PromptOptions::default(), - system_prompt: "".to_string(), prompt_template: None, } } @@ -62,33 +61,98 @@ impl PromptInstruction { assistant }; + // Create a new Conversation in the database + let conversation_id = { + db_conn.new_conversation( + "New Conversation", + None, + serde_json::to_value(&prompt_instruction.completion_options) + .ok(), + serde_json::to_value(&prompt_instruction.prompt_options).ok(), + )? + }; + prompt_instruction + .cache + .set_conversation_id(conversation_id); + if let Some(assistant) = assistant { prompt_instruction.preload_from_assistant( assistant, instruction, // add user-instruction with assistant + db_conn, )?; } else if let Some(instruction) = instruction { - prompt_instruction.set_system_prompt(instruction); + let exchange = prompt_instruction.first_exchange(Some(instruction)); + let _result = + db_conn.finalize_exchange(&exchange, &prompt_instruction.cache); }; - // Create a new Conversation in the database - let conversation_id = - { db_conn.new_conversation("New Conversation", None)? }; - prompt_instruction - .cache - .set_conversation_id(conversation_id); - Ok(prompt_instruction) } + pub fn import_conversation( + &mut self, + id: &str, + db_conn: &ConversationDatabaseStore, + ) -> Result<(), ApplicationError> { + // Fetch the conversation and its messages from the database + let conversation_id = ConversationId(id.parse().map_err(|_| { + ApplicationError::NotFound(format!( + "Conversation {id} not found in database" + )) + })?); + + let (conversation, messages) = db_conn + .fetch_conversation(Some(conversation_id), None)? + .ok_or_else(|| { + ApplicationError::NotFound("Conversation not found".to_string()) + })?; + + // Clear the existing ConversationCache + self.cache = ConversationCache::new(); + + // Set the conversation ID + self.cache.set_conversation_id(conversation.id); + + // Group messages by exchange + let mut exchanges: HashMap> = HashMap::new(); + for message in messages { + exchanges + .entry(message.exchange_id) + .or_default() + .push(message); + } + + // Add exchanges and messages to the cache + for (exchange_id, exchange_messages) in exchanges { + let exchange = Exchange { + id: exchange_id, + conversation_id: conversation.id, + created_at: exchange_messages + .first() + .map(|m| m.created_at) + .unwrap_or(0), + previous_exchange_id: None, // You might want to store and retrieve this + is_deleted: false, + }; + + self.cache.add_exchange(exchange); + + for message in exchange_messages { + self.cache.add_message(message); + } + } + Ok(()) + } pub fn reset_history( &mut self, db_conn: &ConversationDatabaseStore, ) -> Result<(), ApplicationError> { // reset by creating a new conversation + // TODO: clone previous conversation settings let current_conversation_id = - db_conn.new_conversation("New Conversation", None)?; + db_conn.new_conversation("New Conversation", None, None, None)?; self.cache.set_conversation_id(current_conversation_id); Ok(()) } @@ -117,19 +181,34 @@ impl PromptInstruction { } } - fn first_exchange(&self) -> Exchange { - Exchange { + fn first_exchange(&mut self, system_prompt: Option) -> Exchange { + let exchange = Exchange { id: ExchangeId(0), conversation_id: self.cache.get_conversation_id(), - model_id: ModelId(0), - system_prompt: Some(self.system_prompt.clone()), - completion_options: serde_json::to_value(&self.completion_options) - .ok(), - prompt_options: serde_json::to_value(&self.prompt_options).ok(), created_at: 0, previous_exchange_id: None, is_deleted: false, - } + }; + + let system_message = Message { + id: self.cache.new_message_id(), + conversation_id: self.cache.get_conversation_id(), + exchange_id: exchange.id, + role: PromptRole::System, + message_type: "text".to_string(), + has_attachments: false, + token_length: Some(simple_token_estimator( + &system_prompt.as_deref().unwrap_or(""), + None, + )), + content: system_prompt.unwrap_or_else(|| "".to_string()), + created_at: 0, + is_deleted: false, + }; + // add first exchange including system prompt message + self.cache.add_message(system_message); + self.cache.add_exchange(exchange.clone()); + exchange } pub fn subsequent_exchange(&mut self) -> Exchange { @@ -137,43 +216,13 @@ impl PromptInstruction { Exchange { id: self.cache.new_exchange_id(), conversation_id: self.cache.get_conversation_id(), - model_id: last.model_id, - system_prompt: last.system_prompt.clone(), - completion_options: last.completion_options.clone(), - prompt_options: last.prompt_options.clone(), created_at: 0, previous_exchange_id: Some(last.id), is_deleted: false, } } else { - // create first exchange - let exchange = self.first_exchange(); - // add system prompt - let system_message = Message { - id: self.cache.new_message_id(), - conversation_id: self.cache.get_conversation_id(), - exchange_id: exchange.id, - role: PromptRole::System, - message_type: "text".to_string(), - content: self.system_prompt.clone(), - has_attachments: false, - token_length: Some(simple_token_estimator( - &self.system_prompt, - None, - )), - created_at: 0, - is_deleted: false, - }; - // add first exchange including system prompt message - self.cache.add_message(system_message); - self.cache.add_exchange(exchange.clone()); - - // return subsequent exchange - Exchange { - id: self.cache.new_exchange_id(), - previous_exchange_id: Some(exchange.id), - ..exchange - } + // should never happen as first_exchange is always added in new() + unreachable!("subsequent_exchange called before first_exchange"); } } @@ -210,6 +259,8 @@ impl PromptInstruction { let mut messages: Vec = Vec::new(); let mut total_tokens = system_prompt_token_length; + let mut system_message: Option = None; + // Add messages from most recent to oldest, respecting token limit for exchange in current_exchanges.into_iter().rev() { for msg in self @@ -222,6 +273,10 @@ impl PromptInstruction { msg.token_length.map(|len| len as usize).unwrap_or(0); if msg.role == PromptRole::System { + system_message = Some(ChatMessage { + role: msg.role, + content: msg.content.clone(), + }); continue; // system prompt is included separately } if total_tokens + msg_token_length <= max_token_length { @@ -241,12 +296,10 @@ impl PromptInstruction { } // ensure the system prompt is always included - // after reverse, the system prompt will be at the beginning - messages.push(ChatMessage { - role: PromptRole::System, - content: self.system_prompt.to_string(), - }); - + // last, before reverse, so it will be at the beginning + if let Some(system_message) = system_message { + messages.push(system_message); + } // Reverse the messages to maintain chronological order messages.reverse(); messages @@ -276,22 +329,19 @@ impl PromptInstruction { self.completion_options.get_n_keep() } - fn set_system_prompt(&mut self, instruction: String) { - self.system_prompt = instruction; - } - pub fn get_prompt_template(&self) -> Option<&str> { self.prompt_template.as_deref() } - pub fn get_instruction(&self) -> &str { - &self.system_prompt + pub fn get_instruction(&self) -> Option { + self.cache.get_system_prompt() } pub fn preload_from_assistant( &mut self, assistant: String, user_instruction: Option, + db_conn: &ConversationDatabaseStore, ) -> Result<(), ApplicationError> { let assistant_prompts: Vec = serde_yaml::from_str(PERSONAS) .map_err(|e| { @@ -312,58 +362,53 @@ impl PromptInstruction { })?; let system_prompt = build_system_prompt(&prompt, &user_instruction); - self.set_system_prompt(system_prompt.clone()); + //self.set_system_prompt(system_prompt.clone()); if let Some(exchanges) = prompt.exchanges() { // Create a new exchange with the system prompt - let exchange = self.first_exchange(); - let system_message = Message { - id: self.cache.new_message_id(), - conversation_id: self.cache.get_conversation_id(), - exchange_id: exchange.id, - role: PromptRole::System, - message_type: "text".to_string(), - content: system_prompt, - has_attachments: false, - token_length: None, - created_at: 0, - is_deleted: false, - }; - self.cache.add_message(system_message); - self.cache.add_exchange(exchange); + let exchange = self.first_exchange(Some(system_prompt)); + let _result = db_conn.finalize_exchange(&exchange, &self.cache); for loaded_exchange in exchanges.iter() { let exchange = self.subsequent_exchange(); let exchange_id = exchange.id; - self.cache.add_exchange(exchange); - + let content = loaded_exchange.question.clone(); let user_message = Message { id: self.cache.new_message_id(), conversation_id: self.cache.get_conversation_id(), exchange_id, role: PromptRole::User, message_type: "text".to_string(), - content: loaded_exchange.question.clone(), has_attachments: false, - token_length: None, + token_length: Some(simple_token_estimator(&content, None)), + content, created_at: 0, // Use proper timestamp is_deleted: false, }; self.cache.add_message(user_message); + let content = loaded_exchange.answer.clone(); let assistant_message = Message { id: self.cache.new_message_id(), conversation_id: self.cache.get_conversation_id(), exchange_id, role: PromptRole::Assistant, message_type: "text".to_string(), - content: loaded_exchange.answer.clone(), has_attachments: false, - token_length: None, + token_length: Some(simple_token_estimator(&content, None)), + content, created_at: 0, // Use proper timestamp is_deleted: false, }; self.cache.add_message(assistant_message); + // add to exchange must be done before finalizing + // exchange that is used in finalize_exchange is a reference to version + // it just commited to cache + self.cache.add_exchange(exchange); + if let Some(exchange) = self.cache.get_last_exchange() { + let _result = + db_conn.finalize_exchange(&exchange, &self.cache); + } } } diff --git a/lumni/src/apps/builtin/llm/prompt/src/server/llama/mod.rs b/lumni/src/apps/builtin/llm/prompt/src/server/llama/mod.rs index eaf69cee..7c8b908f 100644 --- a/lumni/src/apps/builtin/llm/prompt/src/server/llama/mod.rs +++ b/lumni/src/apps/builtin/llm/prompt/src/server/llama/mod.rs @@ -48,7 +48,9 @@ impl Llama { &self, prompt_instruction: &PromptInstruction, ) -> Option { - let instruction = prompt_instruction.get_instruction(); + let instruction = prompt_instruction + .get_instruction() + .unwrap_or("".to_string()); let system_prompt = LlamaServerSystemPrompt::new( instruction.to_string(),