open-standards/eoc/eoc-rs/crates/eoc-agent/tests/reflexion_memory.rs
Transaction Science 94681289e6 ingest wave A2: 3 crates · op-dispute-evidence + smart-byte-edv + eoc-agent
OpenPay (1 crate, ~2287 LOC, 15 tests)
  op-dispute-evidence  Chargeback lifecycle + evidence packaging across Visa
                       VCR (10.x/11.x/12.x/13.x), Mastercard Mastercom
                       (4837/4853/1240/1442), Amex SafeKey + DRR, Discover DRR,
                       PayPal. CE3.0 compelling-evidence qualifier (120-365d
                       window, ≥2 qualifiers, ≥2 linking matches, 10.4-only).
                       Phase state machine + ReasonCodeCatalog + sealed
                       EvidencePackageBuilder + win-rate scoring.

Smart Byte (1 crate, ~2479 LOC, 44 tests)
  smart-byte-edv       Encrypted Data Vaults (DIF v0.10).
                       JWE ECDH-ES P-256 + HKDF-SHA256-Concat-KDF + AES-256-GCM,
                       multi-recipient; HMAC-SHA-256 encrypted index with
                       AND-equality queries; full ZCAP-LD chain validation;
                       chunked AES-256-GCM streaming with per-chunk AAD;
                       Vault trait + InMemoryVault + one-way pull replication.

EOC (1 crate, ~2065 LOC, 29 tests)
  eoc-agent            Agentic loops with per-step joule accounting.
                       ReAct, Reflexion (PASS/FAIL judge + episodic memory),
                       Tree of Thoughts (BFS/DFS + self-scoring), LATS
                       (MCTS-UCT select/expand/simulate/backprop),
                       Plan-and-Execute, CodeAct (fenced-block + pluggable
                       runtime). Budget enforcement (tokens/joules/tool-calls),
                       trace spans, JouleCounter integration.

Three other batch-5 deferrals (op-ewallets-asia, smart-byte-zk, eoc-rag)
will land in wave A3.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-24 11:42:26 -04:00

62 lines
2 KiB
Rust

//! Integration test: Reflexion turns a failed first attempt into a
//! retry that uses the judge's critique.
use std::sync::Arc;
use std::sync::Mutex;
use async_trait::async_trait;
use eoc_agent::agent::{Agent, Budget, LlmProvider, LlmReply};
use eoc_agent::error::AgentResult;
use eoc_agent::reflexion::ReflexionLoop;
use eoc_agent::StopReason;
struct Scripted {
replies: Mutex<Vec<&'static str>>,
}
#[async_trait]
impl LlmProvider for Scripted {
async fn complete(&self, _prompt: &str) -> AgentResult<LlmReply> {
let text = {
let mut q = self.replies.lock().expect("lock");
if q.is_empty() {
"(empty)".to_string()
} else {
q.remove(0).to_string()
}
};
Ok(LlmReply::new(text, 8, 500))
}
}
#[tokio::test]
async fn reflexion_retries_after_failure() {
// Actor: first attempt is wrong, second attempt is right.
let actor = Arc::new(Scripted {
replies: Mutex::new(vec!["answer: 41", "answer: 42"]),
});
// Judge: FAILs the first, PASSes the second.
let judge = Arc::new(Scripted {
replies: Mutex::new(vec!["FAIL\noff by one", "PASS\nlooks right"]),
});
let mut agent = ReflexionLoop::new(actor, judge, Budget::unlimited(10), 5);
let (out, stop) = agent.run("what is 6 * 7?").await.expect("ok");
assert_eq!(out, "answer: 42");
assert_eq!(stop, StopReason::Done);
assert_eq!(agent.memory.lessons.len(), 1);
assert!(agent.memory.lessons[0].contains("off by one"));
}
#[tokio::test]
async fn reflexion_gives_up_after_max_attempts() {
let actor = Arc::new(Scripted {
replies: Mutex::new(vec!["bad", "bad", "bad"]),
});
let judge = Arc::new(Scripted {
replies: Mutex::new(vec!["FAIL a", "FAIL b", "FAIL c"]),
});
let mut agent = ReflexionLoop::new(actor, judge, Budget::unlimited(10), 2);
let (_out, stop) = agent.run("goal").await.expect("ok");
assert_eq!(stop, StopReason::MaxIterations);
assert_eq!(agent.memory.lessons.len(), 2);
}