use chrono::{DateTime, Utc};
pub const MAX_FLOW_ITEMS: usize = 500;
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct StructuredContext {
pub key_decisions: Vec<DecisionItem>,
pub open_questions: Vec<QuestionItem>,
pub key_concepts: Vec<ConceptItem>,
pub technical_specifications: Vec<SpecItem>,
pub conversation_flow: Vec<FlowItem>,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct DecisionItem {
pub description: String,
pub context: String,
pub alternatives: Vec<String>,
pub confidence: f32,
pub timestamp: DateTime<Utc>,
}
impl DecisionItem {
pub fn new(
description: String,
context: String,
alternatives: Vec<String>,
confidence: f32,
timestamp: DateTime<Utc>,
) -> Self {
Self {
description,
context,
alternatives,
confidence: confidence.clamp(0.0, 1.0),
timestamp,
}
}
}
impl Default for DecisionItem {
fn default() -> Self {
Self {
description: String::new(),
context: String::new(),
alternatives: Vec::new(),
confidence: 0.5,
timestamp: Utc::now(),
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct QuestionItem {
pub question: String,
pub context: String,
pub status: QuestionStatus,
pub timestamp: DateTime<Utc>,
pub last_updated: DateTime<Utc>,
}
impl Default for QuestionItem {
fn default() -> Self {
let now = Utc::now();
Self {
question: String::new(),
context: String::new(),
status: QuestionStatus::Open,
timestamp: now,
last_updated: now,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
pub enum QuestionStatus {
#[default]
Open,
InProgress,
Answered,
Deferred,
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct ConceptItem {
pub name: String,
pub definition: String,
pub examples: Vec<String>,
pub related_concepts: Vec<String>,
pub timestamp: DateTime<Utc>,
}
impl Default for ConceptItem {
fn default() -> Self {
Self {
name: String::new(),
definition: String::new(),
examples: Vec::new(),
related_concepts: Vec::new(),
timestamp: Utc::now(),
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct SpecItem {
pub title: String,
pub description: String,
pub requirements: Vec<String>,
pub constraints: Vec<String>,
pub timestamp: DateTime<Utc>,
}
impl Default for SpecItem {
fn default() -> Self {
Self {
title: String::new(),
description: String::new(),
requirements: Vec::new(),
constraints: Vec::new(),
timestamp: Utc::now(),
}
}
}
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct FlowItem {
pub step_description: String,
pub timestamp: DateTime<Utc>,
pub related_updates: Vec<uuid::Uuid>,
pub outcome: Option<String>,
}
impl Default for FlowItem {
fn default() -> Self {
Self {
step_description: String::new(),
timestamp: Utc::now(),
related_updates: Vec::new(),
outcome: None,
}
}
}
impl Default for StructuredContext {
fn default() -> Self {
Self::new()
}
}
impl StructuredContext {
pub fn new() -> Self {
Self {
key_decisions: Vec::new(),
open_questions: Vec::new(),
key_concepts: Vec::new(),
technical_specifications: Vec::new(),
conversation_flow: Vec::new(),
}
}
pub fn add_flow_item(&mut self, item: FlowItem) {
if self.conversation_flow.len() >= MAX_FLOW_ITEMS {
let remove_count = MAX_FLOW_ITEMS / 10;
self.conversation_flow.drain(0..remove_count);
}
self.conversation_flow.push(item);
}
pub fn total_items(&self) -> usize {
self.key_decisions.len()
+ self.open_questions.len()
+ self.key_concepts.len()
+ self.technical_specifications.len()
+ self.conversation_flow.len()
}
pub fn is_empty(&self) -> bool {
self.total_items() == 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decision_item_confidence_clamping() {
let item = DecisionItem::new(
"Test".to_string(),
"Context".to_string(),
vec![],
1.5, Utc::now(),
);
assert_eq!(item.confidence, 1.0);
let item2 = DecisionItem::new(
"Test".to_string(),
"Context".to_string(),
vec![],
-0.5, Utc::now(),
);
assert_eq!(item2.confidence, 0.0);
}
#[test]
fn test_flow_item_limit() {
let mut ctx = StructuredContext::new();
for i in 0..(MAX_FLOW_ITEMS + 100) {
ctx.add_flow_item(FlowItem {
step_description: format!("Step {}", i),
..Default::default()
});
}
assert!(ctx.conversation_flow.len() <= MAX_FLOW_ITEMS);
}
#[test]
fn test_structured_context_is_empty() {
let ctx = StructuredContext::new();
assert!(ctx.is_empty());
assert_eq!(ctx.total_items(), 0);
}
#[test]
fn test_default_implementations() {
let _ = DecisionItem::default();
let _ = QuestionItem::default();
let _ = ConceptItem::default();
let _ = SpecItem::default();
let _ = FlowItem::default();
let _ = QuestionStatus::default();
}
}