use crate::message::Message;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SelfModel {
#[serde(default)]
pub self_context: Vec<Message>,
#[serde(default)]
pub user_context: Vec<Message>,
#[serde(default)]
pub collective_context: Vec<Message>,
}
impl SelfModel {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_self_context(mut self, messages: Vec<Message>) -> Self {
self.self_context = messages;
self
}
#[must_use]
pub fn with_user_context(mut self, messages: Vec<Message>) -> Self {
self.user_context = messages;
self
}
#[must_use]
pub fn with_collective_context(mut self, messages: Vec<Message>) -> Self {
self.collective_context = messages;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct NegativeKnowledge {
pub category: String,
pub constraint: String,
pub severity: Severity,
pub source: String,
#[serde(default)]
pub context: String,
#[serde(default)]
pub created_at: String,
}
impl NegativeKnowledge {
pub fn new(
category: impl Into<String>,
constraint: impl Into<String>,
severity: Severity,
) -> Self {
Self {
category: category.into(),
constraint: constraint.into(),
severity,
source: String::new(),
context: String::new(),
created_at: String::new(),
}
}
#[must_use]
pub fn with_source(mut self, source: impl Into<String>) -> Self {
self.source = source.into();
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum Severity {
Critical,
High,
Medium,
Low,
}
impl Severity {
pub fn is_at_least(&self, minimum: &Severity) -> bool {
self <= minimum
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct FailureRecord {
pub task_type: String,
pub approach_tried: String,
#[serde(default)]
pub error_kind: String,
#[serde(default)]
pub root_cause: Option<String>,
#[serde(default)]
pub resolution: Option<String>,
#[serde(default)]
pub timestamp: String,
#[serde(default)]
pub scope_id: String,
}
impl FailureRecord {
pub fn new(task_type: impl Into<String>, approach_tried: impl Into<String>) -> Self {
Self {
task_type: task_type.into(),
approach_tried: approach_tried.into(),
error_kind: String::new(),
root_cause: None,
resolution: None,
timestamp: String::new(),
scope_id: String::new(),
}
}
#[must_use]
pub fn with_error_kind(mut self, kind: impl Into<String>) -> Self {
self.error_kind = kind.into();
self
}
#[must_use]
pub fn with_root_cause(mut self, cause: impl Into<String>) -> Self {
self.root_cause = Some(cause.into());
self
}
#[must_use]
pub fn with_resolution(mut self, resolution: impl Into<String>) -> Self {
self.resolution = Some(resolution.into());
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_self_model_creation() {
let model = SelfModel::new()
.with_self_context(vec![Message::system("I analyze code.")])
.with_user_context(vec![Message::system("User is a senior dev.")]);
assert_eq!(model.self_context.len(), 1);
assert_eq!(model.user_context.len(), 1);
assert!(model.collective_context.is_empty());
}
#[test]
fn test_negative_knowledge() {
let nk = NegativeKnowledge::new("api", "max 100 items per batch", Severity::High)
.with_source("production incident");
assert_eq!(nk.category, "api");
assert_eq!(nk.severity, Severity::High);
assert_eq!(nk.source, "production incident");
}
#[test]
fn test_severity_ordering() {
assert!(Severity::Critical < Severity::High);
assert!(Severity::High < Severity::Medium);
assert!(Severity::Medium < Severity::Low);
}
#[test]
fn test_failure_record() {
let record = FailureRecord::new("db_migration", "ALTER TABLE")
.with_error_kind("timeout")
.with_root_cause("table too large")
.with_resolution("use pt-online-schema-change");
assert_eq!(record.task_type, "db_migration");
assert_eq!(record.error_kind, "timeout");
assert_eq!(record.root_cause.as_deref(), Some("table too large"));
assert_eq!(
record.resolution.as_deref(),
Some("use pt-online-schema-change")
);
}
#[test]
fn test_negative_knowledge_serialization() {
let nk = NegativeKnowledge::new("parsing", "don't use regex for HTML", Severity::Critical);
let json = serde_json::to_string(&nk).unwrap();
let back: NegativeKnowledge = serde_json::from_str(&json).unwrap();
assert_eq!(back, nk);
}
#[test]
fn test_failure_record_serialization() {
let record = FailureRecord::new("auth", "JWT validation").with_error_kind("expired_token");
let json = serde_json::to_string(&record).unwrap();
let back: FailureRecord = serde_json::from_str(&json).unwrap();
assert_eq!(back, record);
}
}