haki-core 0.1.0

Agent loop and session management for haki
Documentation
//! Session — a single conversation context.

use uuid::Uuid;
use chrono::Utc;
use haki_llm::Message;
use haki_config::Config;

#[derive(Debug, Clone)]
pub struct Session {
    pub id: String,
    pub created_at: String,
    pub model: String,
    pub history: Vec<Message>,
    pub config: Config,
}

impl Session {
    pub fn new(config: Config) -> Self {
        Self {
            id: Uuid::new_v4().to_string(),
            created_at: Utc::now().to_rfc3339(),
            model: config.model.clone(),
            history: Vec::new(),
            config,
        }
    }

    pub fn push(&mut self, msg: Message) {
        self.history.push(msg);
    }

    pub fn history(&self) -> &[Message] {
        &self.history
    }

    pub fn last_assistant_message(&self) -> Option<&str> {
        self.history
            .iter()
            .rev()
            .find(|m| m.role == haki_llm::Role::Assistant)
            .map(|m| m.content.as_str())
    }
}

// ─── Tests ────────────────────────────────────────────────────────────────────

#[cfg(test)]
mod tests {
    use super::*;
    use haki_llm::Message;

    fn test_config() -> Config {
        Config::default()
    }

    #[test]
    fn session_has_unique_id() {
        let s1 = Session::new(test_config());
        let s2 = Session::new(test_config());
        assert_ne!(s1.id, s2.id);
    }

    #[test]
    fn push_and_history() {
        let mut s = Session::new(test_config());
        s.push(Message::user("hello"));
        s.push(Message::assistant("hi"));
        assert_eq!(s.history().len(), 2);
    }

    #[test]
    fn last_assistant_message() {
        let mut s = Session::new(test_config());
        s.push(Message::user("ping"));
        s.push(Message::assistant("pong"));
        assert_eq!(s.last_assistant_message(), Some("pong"));
    }

    #[test]
    fn last_assistant_message_none_when_empty() {
        let s = Session::new(test_config());
        assert!(s.last_assistant_message().is_none());
    }
}