#![allow(unused_imports)]
use std::sync::Arc;
use foundation_ai::agentic::context::ContextProvider;
use foundation_ai::agentic::context::ContextConfig;
use foundation_ai::agentic::memory_store::{KvMemoryStore, MemoryStore};
use foundation_ai::agentic::message_api::MessageApi;
use foundation_ai::agentic::token_ledger::TokenLedger;
use foundation_ai::types::{
MemoryFact, MessageRole, Messages, ModelOutput, ObservationEntry, ObservationKind,
ReflectionEntry, SessionId, SessionRecord, TextContent, UserModelContent,
};
use foundation_compact::SystemTime;
use foundation_db::{MemoryDocumentStore, MemoryStorage};
fn make_provider() -> ContextProvider<MemoryDocumentStore, KvMemoryStore<MemoryStorage>> {
let doc_store = MemoryDocumentStore::new();
let kv_store = KvMemoryStore::new(MemoryStorage::new());
let session_id = SessionId::new();
let message_api = MessageApi::new(session_id.clone(), doc_store);
let ledger = TokenLedger::new();
ContextProvider::new(
session_id,
message_api,
Arc::new(kv_store),
ledger,
Some("You are a helpful assistant.".into()),
ContextConfig::default(),
)
}
fn working_record(fact: &str) -> SessionRecord {
SessionRecord::WorkingMemory {
id: foundation_compact::ids::new_scru128(),
facts: vec![MemoryFact {
fact: fact.into(),
asserted_at: SystemTime::UNIX_EPOCH,
source_message_id: foundation_compact::ids::new_scru128(),
confidence: 0.9,
}],
version: 1,
timestamp: SystemTime::UNIX_EPOCH,
}
}
fn observation_record(content: &str, _ts: SystemTime) -> SessionRecord {
SessionRecord::Observation {
id: foundation_compact::ids::new_scru128(),
observations: vec![ObservationEntry {
kind: ObservationKind::Assertion,
content: content.into(),
timestamp: _ts,
source_message_id: foundation_compact::ids::new_scru128(),
scope: None,
}],
token_count: 5,
timestamp: _ts,
}
}
fn reflection_record(summary: &str, _ts: SystemTime) -> SessionRecord {
SessionRecord::Reflection {
id: foundation_compact::ids::new_scru128(),
reflections: vec![ReflectionEntry {
summary: summary.into(),
time_range: None,
observation_refs: vec![],
importance: 0.8,
}],
generated_at: _ts,
observation_token_count_before: 5,
reflection_token_count_after: 2,
}
}
fn user_message(text: &str) -> Messages {
Messages::User {
id: foundation_compact::ids::new_scru128(),
role: MessageRole::User,
content: UserModelContent::Text(TextContent {
content: text.into(),
signature: None,
}),
signature: None,
}
}
#[test]
fn empty_context_has_system_prompt_only() {
use futures_lite::future::block_on;
let provider = make_provider();
let ctx = block_on(provider.assemble());
assert_eq!(
ctx.system_prompt.as_deref(),
Some("You are a helpful assistant.")
);
assert!(ctx.messages.is_empty());
}
#[test]
fn working_memory_injected() {
use futures_lite::future::block_on;
let provider = make_provider();
block_on(async {
provider
.memory_store()
.set_async(provider.session_id(), &working_record("user likes rust"))
.await
.unwrap();
let ctx = provider.assemble().await;
assert_eq!(ctx.messages.len(), 1);
let text = extract_text(&ctx.messages[0]);
assert!(text.contains("user likes rust"));
});
}
#[test]
fn incon03_observation_injected_when_no_reflection() {
use futures_lite::future::block_on;
let provider = make_provider();
block_on(async {
let obs = observation_record("saw something", SystemTime::UNIX_EPOCH);
provider
.memory_store()
.set_async(provider.session_id(), &obs)
.await
.unwrap();
let ctx = provider.assemble().await;
assert_eq!(ctx.messages.len(), 1);
let text = extract_text(&ctx.messages[0]);
assert!(text.contains("saw something"));
});
}
#[test]
fn incon03_observation_suppressed_when_reflection_newer() {
use futures_lite::future::block_on;
let provider = make_provider();
block_on(async {
let t1 = SystemTime::UNIX_EPOCH;
let t2 = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(100);
let obs = observation_record("old observation", t1);
let refl = reflection_record("newer reflection", t2);
provider
.memory_store()
.set_async(provider.session_id(), &obs)
.await
.unwrap();
provider
.memory_store()
.set_async(provider.session_id(), &refl)
.await
.unwrap();
let ctx = provider.assemble().await;
let texts: Vec<String> = ctx.messages.iter().map(|m| extract_text(m)).collect();
assert!(
texts.iter().any(|t| t.contains("newer reflection")),
"reflection should be present"
);
assert!(
!texts.iter().any(|t| t.contains("old observation")),
"old observation should be suppressed"
);
});
}
#[test]
fn incon03_observation_injected_when_newer_than_reflection() {
use futures_lite::future::block_on;
let provider = make_provider();
block_on(async {
let t1 = SystemTime::UNIX_EPOCH;
let t2 = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(100);
let refl = reflection_record("old reflection", t1);
let obs = observation_record("newer observation", t2);
provider
.memory_store()
.set_async(provider.session_id(), &refl)
.await
.unwrap();
provider
.memory_store()
.set_async(provider.session_id(), &obs)
.await
.unwrap();
let ctx = provider.assemble().await;
let texts: Vec<String> = ctx.messages.iter().map(|m| extract_text(m)).collect();
assert!(texts.iter().any(|t| t.contains("old reflection")));
assert!(texts.iter().any(|t| t.contains("newer observation")));
});
}
#[test]
fn recent_messages_included() {
use futures_lite::future::block_on;
let provider = make_provider();
block_on(async {
let msg = user_message("hello world");
let _ = provider
.message_api()
.append(SessionRecord::Conversation { message: msg });
let _ = provider.message_api().flush();
let ctx = provider.assemble().await;
assert_eq!(ctx.messages.len(), 1);
let text = extract_text(&ctx.messages[0]);
assert!(text.contains("hello world"));
});
}
#[test]
fn assembly_order_is_deterministic() {
use futures_lite::future::block_on;
let provider = make_provider();
block_on(async {
let t1 = SystemTime::UNIX_EPOCH;
let t2 = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(100);
provider
.memory_store()
.set_async(provider.session_id(), &working_record("fact A"))
.await
.unwrap();
provider
.memory_store()
.set_async(provider.session_id(), &reflection_record("reflection B", t1))
.await
.unwrap();
provider
.memory_store()
.set_async(
provider.session_id(),
&observation_record("observation C", t2),
)
.await
.unwrap();
let msg = user_message("user message D");
let _ = provider
.message_api()
.append(SessionRecord::Conversation { message: msg });
let _ = provider.message_api().flush();
let ctx = provider.assemble().await;
assert_eq!(ctx.messages.len(), 4);
let texts: Vec<String> = ctx.messages.iter().map(|m| extract_text(m)).collect();
assert!(
texts[0].contains("fact A"),
"first should be working memory"
);
assert!(
texts[1].contains("reflection B"),
"second should be reflection"
);
assert!(
texts[2].contains("observation C"),
"third should be observation (newer than reflection)"
);
assert!(
texts[3].contains("user message D"),
"fourth should be recent message"
);
});
}
#[test]
fn clone_shares_state() {
let provider = make_provider();
let _cloned = provider.clone();
}
fn extract_text(msg: &Messages) -> String {
match msg {
Messages::User { content, .. } | Messages::ToolResult { content, .. } => {
match content {
UserModelContent::Text(tc) => tc.content.clone(),
_ => String::new(),
}
}
Messages::Assistant { content, .. } => match content {
ModelOutput::Text(tc) => tc.content.clone(),
_ => String::new(),
},
}
}