feat: add agent structure and improve tooling for provider

This commit is contained in:
mai1015 2026-02-16 00:40:43 -05:00 committed by Chummy
parent e2c966d31e
commit b341fdb368
21 changed files with 2567 additions and 443 deletions

118
src/agent/memory_loader.rs Normal file
View file

@ -0,0 +1,118 @@
use crate::memory::Memory;
use async_trait::async_trait;
use std::fmt::Write;
#[async_trait]
pub trait MemoryLoader: Send + Sync {
async fn load_context(&self, memory: &dyn Memory, user_message: &str)
-> anyhow::Result<String>;
}
pub struct DefaultMemoryLoader {
limit: usize,
}
impl Default for DefaultMemoryLoader {
fn default() -> Self {
Self { limit: 5 }
}
}
impl DefaultMemoryLoader {
pub fn new(limit: usize) -> Self {
Self {
limit: limit.max(1),
}
}
}
#[async_trait]
impl MemoryLoader for DefaultMemoryLoader {
async fn load_context(
&self,
memory: &dyn Memory,
user_message: &str,
) -> anyhow::Result<String> {
let entries = memory.recall(user_message, self.limit).await?;
if entries.is_empty() {
return Ok(String::new());
}
let mut context = String::from("[Memory context]\n");
for entry in entries {
let _ = writeln!(context, "- {}: {}", entry.key, entry.content);
}
context.push('\n');
Ok(context)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::memory::{Memory, MemoryCategory, MemoryEntry};
struct MockMemory;
#[async_trait]
impl Memory for MockMemory {
async fn store(
&self,
_key: &str,
_content: &str,
_category: MemoryCategory,
) -> anyhow::Result<()> {
Ok(())
}
async fn recall(&self, _query: &str, limit: usize) -> anyhow::Result<Vec<MemoryEntry>> {
if limit == 0 {
return Ok(vec![]);
}
Ok(vec![MemoryEntry {
id: "1".into(),
key: "k".into(),
content: "v".into(),
category: MemoryCategory::Conversation,
timestamp: "now".into(),
session_id: None,
score: None,
}])
}
async fn get(&self, _key: &str) -> anyhow::Result<Option<MemoryEntry>> {
Ok(None)
}
async fn list(
&self,
_category: Option<&MemoryCategory>,
) -> anyhow::Result<Vec<MemoryEntry>> {
Ok(vec![])
}
async fn forget(&self, _key: &str) -> anyhow::Result<bool> {
Ok(true)
}
async fn count(&self) -> anyhow::Result<usize> {
Ok(0)
}
async fn health_check(&self) -> bool {
true
}
fn name(&self) -> &str {
"mock"
}
}
#[tokio::test]
async fn default_loader_formats_context() {
let loader = DefaultMemoryLoader::default();
let context = loader.load_context(&MockMemory, "hello").await.unwrap();
assert!(context.contains("[Memory context]"));
assert!(context.contains("- k: v"));
}
}