use crate::core::context_update::{ContextUpdate, EntityType};
use crate::session::active_session::UserPreferences;
use chrono::{DateTime, Utc};
use dashmap::DashMap;
use serde::{Deserialize, Serialize};
use std::collections::VecDeque;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use uuid::Uuid;
#[derive(Debug)]
pub struct HotContext {
updates: DashMap<u64, ContextUpdate>,
next_id: Arc<AtomicU64>,
max_size: usize,
}
impl HotContext {
pub fn new(max_size: usize) -> Self {
Self {
updates: DashMap::new(),
next_id: Arc::new(AtomicU64::new(0)),
max_size,
}
}
pub fn push(&self, update: ContextUpdate) {
let id = self.next_id.fetch_add(1, Ordering::Release);
self.updates.insert(id, update);
if id >= self.max_size as u64 {
let to_evict = id - self.max_size as u64;
self.updates.remove(&to_evict);
}
}
pub fn get_recent(&self, n: usize) -> Vec<ContextUpdate> {
let current_id = self.next_id.load(Ordering::Acquire);
let effective_n = n.min(self.max_size);
let start_id = current_id.saturating_sub(effective_n as u64);
let mut results = Vec::with_capacity(effective_n);
for id in (start_id..current_id).rev() {
if let Some(entry) = self.updates.get(&id) {
results.push(entry.clone());
}
}
results
}
pub fn get_all(&self) -> Vec<ContextUpdate> {
let current_id = self.next_id.load(Ordering::Acquire);
let start_id = current_id.saturating_sub(self.max_size as u64);
let mut results = Vec::with_capacity(self.max_size.min(current_id as usize));
for id in start_id..current_id {
if let Some(entry) = self.updates.get(&id) {
results.push(entry.clone());
}
}
results
}
pub fn len(&self) -> usize {
self.updates.len()
}
pub fn is_empty(&self) -> bool {
self.updates.is_empty()
}
pub fn clear(&self) {
self.updates.clear();
self.next_id.store(0, Ordering::Release);
}
pub fn remove_by_id(&self, entry_id: &uuid::Uuid) -> bool {
let slot = self
.updates
.iter()
.find(|entry| entry.value().id == *entry_id)
.map(|entry| *entry.key());
if let Some(key) = slot {
self.updates.remove(&key).is_some()
} else {
false
}
}
pub fn back(&self) -> Option<ContextUpdate> {
let current_id = self.next_id.load(Ordering::Acquire);
if current_id == 0 {
return None;
}
self.updates.get(&(current_id - 1)).map(|e| e.clone())
}
pub fn snapshot(&self) -> Vec<ContextUpdate> {
self.get_all()
}
pub fn iter(&self) -> Vec<ContextUpdate> {
self.snapshot()
}
pub fn to_deque(&self) -> VecDeque<ContextUpdate> {
let current_id = self.next_id.load(Ordering::Acquire);
let start_id = current_id.saturating_sub(self.max_size as u64);
let mut deque = VecDeque::with_capacity(self.max_size.min(current_id as usize));
for id in start_id..current_id {
if let Some(entry) = self.updates.get(&id) {
deque.push_back(entry.clone());
}
}
deque
}
pub fn from_deque(deque: VecDeque<ContextUpdate>, max_size: usize) -> Self {
let hot = Self::new(max_size);
for update in deque {
hot.push(update);
}
hot
}
}
pub struct EntityGraph {
pub entities: DashMap<String, EntityNode>,
relationships: DashMap<u64, crate::core::context_update::EntityRelationship>,
next_rel_id: Arc<AtomicU64>,
}
#[derive(Clone, Debug)]
pub struct EntityNode {
pub entity_type: EntityType,
pub mention_count: Arc<AtomicUsize>,
pub first_mentioned: Arc<AtomicU64>,
pub last_mentioned: Arc<AtomicU64>,
pub importance_score: Arc<std::sync::atomic::AtomicU32>,
pub description: Option<String>,
}
impl EntityGraph {
pub fn new() -> Self {
Self {
entities: DashMap::new(),
relationships: DashMap::new(),
next_rel_id: Arc::new(AtomicU64::new(0)),
}
}
pub fn add_or_update_entity(
&self,
name: String,
entity_type: EntityType,
timestamp: DateTime<Utc>,
description: &str,
) {
let timestamp_secs = timestamp.timestamp() as u64;
self.entities
.entry(name.clone())
.and_modify(|node| {
node.mention_count.fetch_add(1, Ordering::Relaxed);
node.last_mentioned.store(timestamp_secs, Ordering::Relaxed);
})
.or_insert(EntityNode {
entity_type,
mention_count: Arc::new(AtomicUsize::new(1)),
first_mentioned: Arc::new(AtomicU64::new(timestamp_secs)),
last_mentioned: Arc::new(AtomicU64::new(timestamp_secs)),
importance_score: Arc::new(std::sync::atomic::AtomicU32::new(1.0f32.to_bits())),
description: if description.is_empty() {
None
} else {
Some(description.to_string())
},
});
}
pub fn mention_entity(&self, name: &str, _update_id: uuid::Uuid, timestamp: DateTime<Utc>) {
let timestamp_secs = timestamp.timestamp() as u64;
if let Some(node) = self.entities.get_mut(name) {
node.mention_count.fetch_add(1, Ordering::Relaxed);
node.last_mentioned.store(timestamp_secs, Ordering::Relaxed);
}
}
pub fn add_relationship(&self, relationship: crate::core::context_update::EntityRelationship) {
let id = self.next_rel_id.fetch_add(1, Ordering::Relaxed);
self.relationships.insert(id, relationship);
}
pub fn get_entity(&self, name: &str) -> Option<EntityNode> {
self.entities.get(name).map(|e| e.clone())
}
pub fn get_all_entities(&self) -> Vec<(String, EntityType, usize)> {
self.entities
.iter()
.map(|entry| {
let name = entry.key().clone();
let node = entry.value();
let count = node.mention_count.load(Ordering::Relaxed);
(name, node.entity_type.clone(), count)
})
.collect()
}
pub fn get_relationships(&self) -> Vec<crate::core::context_update::EntityRelationship> {
self.relationships
.iter()
.map(|entry| entry.value().clone())
.collect()
}
pub fn entity_count(&self) -> usize {
self.entities.len()
}
pub fn relationship_count(&self) -> usize {
self.relationships.len()
}
pub fn clear(&self) {
self.entities.clear();
self.relationships.clear();
self.next_rel_id.store(0, Ordering::Relaxed);
}
}
impl Default for EntityGraph {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SessionMetadata {
pub id: Uuid,
pub name: Option<String>,
pub description: Option<String>,
pub created_at: DateTime<Utc>,
pub user_preferences: UserPreferences,
}
impl SessionMetadata {
pub fn new(
id: Uuid,
name: Option<String>,
description: Option<String>,
user_preferences: UserPreferences,
) -> Self {
Self {
id,
name,
description,
created_at: Utc::now(),
user_preferences,
}
}
pub fn with_created_at(
id: Uuid,
name: Option<String>,
description: Option<String>,
user_preferences: UserPreferences,
created_at: DateTime<Utc>,
) -> Self {
Self {
id,
name,
description,
created_at,
user_preferences,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::context_update::{RelationType, UpdateContent, UpdateType};
#[test]
fn test_hot_context() {
let hot = HotContext::new(3);
assert_eq!(hot.len(), 0);
assert!(hot.is_empty());
for i in 0..5 {
let update = ContextUpdate {
id: Uuid::new_v4(),
timestamp: Utc::now(),
update_type: UpdateType::QuestionAnswered,
content: UpdateContent {
title: format!("Question {}", i),
description: format!("Answer {}", i),
details: vec![],
examples: vec![],
implications: vec![],
},
related_code: None,
parent_update: None,
user_marked_important: false,
creates_entities: vec![],
creates_relationships: vec![],
references_entities: vec![],
typed_entities: vec![],
};
hot.push(update);
}
assert_eq!(hot.len(), 3);
let recent = hot.get_recent(2);
assert_eq!(recent.len(), 2);
}
#[test]
fn test_entity_graph() {
use crate::core::context_update::EntityRelationship;
let graph = EntityGraph::new();
assert_eq!(graph.entity_count(), 0);
let now = Utc::now();
graph.add_or_update_entity(
"Rust".to_string(),
EntityType::Technology,
now,
"A systems programming language",
);
graph.add_or_update_entity(
"PostgreSQL".to_string(),
EntityType::Technology,
now,
"A relational database",
);
graph.add_or_update_entity(
"Rust".to_string(),
EntityType::Technology,
now,
"", );
assert_eq!(graph.entity_count(), 2);
let rust = graph.get_entity("Rust").unwrap();
assert_eq!(rust.mention_count.load(Ordering::Relaxed), 2);
assert_eq!(
rust.description,
Some("A systems programming language".to_string())
);
graph.add_relationship(EntityRelationship {
from_entity: "Rust".to_string(),
to_entity: "PostgreSQL".to_string(),
relation_type: RelationType::RelatedTo,
context: "Used together in projects".to_string(),
});
assert_eq!(graph.relationship_count(), 1);
}
#[test]
fn test_session_metadata() {
let prefs = UserPreferences {
auto_save_enabled: true,
context_retention_days: 30,
max_hot_context_size: 50,
auto_summary_threshold: 100,
important_keywords: vec![],
};
let meta = SessionMetadata::new(Uuid::new_v4(), Some("test".to_string()), None, prefs);
assert_eq!(meta.name, Some("test".to_string()));
}
}