a3s-code-core 8.2.0

A3S Code Core - Embeddable AI agent library with tool execution
Documentation
use a3s_code_core::config::{CodeConfig, ModelConfig, ModelModalities, ProviderConfig};
use a3s_code_core::host_env::{FixedClock, HostEnv, SequentialIdGenerator};
use a3s_code_core::memory::MemoryConfig;
use a3s_code_core::{
    Agent, DurableMemoryRecallPolicy, DurableMemorySession, PlanningMode, SessionOptions,
    DURABLE_MEMORY_RETRIEVAL_PROFILE_V1,
};
use a3s_memory::repository::{
    DurableMemoryKind, EvidenceKind, EvidenceRef, InMemoryRepository, MemoryChangeSet,
    MemoryNamespace, MemoryNodeDraft, MemoryOperation, MemoryRepository, MemoryStatus,
};
use a3s_memory::InMemoryStore;
use chrono::{TimeZone, Utc};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::sync::Arc;

const FIXTURE: &str = include_str!("fixtures/durable-memory-multilingual-v1/evaluation.json");

#[path = "durable_memory_multilingual_eval/model.rs"]
mod model;
use model::InspectingClient;

#[derive(Debug, Deserialize)]
struct Fixture {
    schema_version: u32,
    retrieval_profile: String,
    policy: Policy,
    nodes: Vec<Node>,
    foreign_node: Node,
    queries: Vec<Query>,
    negative_queries: Vec<NegativeQuery>,
    thresholds: Thresholds,
}

#[derive(Debug, Deserialize)]
struct Policy {
    max_results: usize,
    min_lexical_score: f32,
    max_related_lookups: usize,
}

#[derive(Debug, Deserialize)]
struct Node {
    id: String,
    kind: DurableMemoryKind,
    status: MemoryStatus,
    content: String,
}

#[derive(Debug, Deserialize)]
struct Query {
    id: String,
    #[serde(rename = "language")]
    _language: String,
    query: String,
    relevant_node_id: String,
}

#[derive(Debug, Deserialize)]
struct NegativeQuery {
    id: String,
    query: String,
}

#[derive(Debug, Deserialize)]
struct Thresholds {
    minimum_recall_at_3: f64,
    minimum_mean_reciprocal_rank: f64,
    maximum_context_nodes_per_query: usize,
    maximum_model_calls_per_query: usize,
    expected_admissions: u64,
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct Report<'a> {
    schema_version: u32,
    retrieval_profile: &'a str,
    queries: usize,
    recall_at_3: f64,
    mean_reciprocal_rank: f64,
    model_calls: usize,
    admissions: u64,
    maximum_context_nodes: usize,
    negative_queries_with_hits: usize,
    candidate_or_foreign_leaks: usize,
}

fn fixture_time() -> chrono::DateTime<Utc> {
    Utc.with_ymd_and_hms(2026, 8, 29, 20, 0, 0)
        .single()
        .expect("valid multilingual fixture time")
}

fn evidence(node: &Node) -> EvidenceRef {
    let kind = if node.status == MemoryStatus::Active {
        EvidenceKind::Verification
    } else {
        EvidenceKind::SessionTurn
    };
    EvidenceRef::try_new(
        format!("a3s://fixture/durable-memory-multilingual-v1/{}", node.id),
        format!("sha256:{:x}", Sha256::digest(node.content.as_bytes())),
        kind,
        fixture_time(),
    )
    .expect("valid multilingual fixture evidence")
}

fn draft(node: &Node, namespace: &MemoryNamespace) -> MemoryNodeDraft {
    MemoryNodeDraft::new(
        &node.id,
        namespace.clone(),
        node.kind,
        node.status,
        &node.content,
        vec![evidence(node)],
        fixture_time(),
    )
}

fn offline_config() -> CodeConfig {
    CodeConfig {
        default_model: Some("anthropic/claude-sonnet-4-20250514".to_string()),
        providers: vec![ProviderConfig {
            name: "anthropic".to_string(),
            api_key: Some("offline-key".to_string()),
            base_url: None,
            headers: HashMap::new(),
            session_id_header: None,
            models: vec![ModelConfig {
                id: "claude-sonnet-4-20250514".to_string(),
                name: "Claude Sonnet 4".to_string(),
                family: "claude-sonnet".to_string(),
                api_key: None,
                base_url: None,
                headers: HashMap::new(),
                session_id_header: None,
                attachment: false,
                reasoning: false,
                tool_call: true,
                temperature: true,
                release_date: None,
                modalities: ModelModalities::default(),
                cost: Default::default(),
                limit: Default::default(),
            }],
        }],
        memory: Some(MemoryConfig {
            llm_extraction: false,
            ..Default::default()
        }),
        ..Default::default()
    }
}

fn host_env() -> Arc<HostEnv> {
    Arc::new(HostEnv::new(
        Arc::new(SequentialIdGenerator::new("multilingual")),
        Arc::new(FixedClock::new(
            u64::try_from(fixture_time().timestamp_millis()).unwrap(),
        )),
    ))
}

async fn seed(fixture: &Fixture) -> (Arc<InMemoryRepository>, MemoryNamespace) {
    let repository = Arc::new(InMemoryRepository::new());
    let namespace = MemoryNamespace::try_new("fixture-tenant", "fixture-principal", "workspace")
        .expect("valid multilingual namespace");
    repository
        .apply(MemoryChangeSet::new(
            "seed-durable-memory-multilingual-v1",
            namespace.clone(),
            fixture_time(),
            fixture
                .nodes
                .iter()
                .map(|node| MemoryOperation::Create {
                    node: draft(node, &namespace),
                })
                .collect(),
        ))
        .await
        .expect("seed multilingual corpus");
    let foreign_namespace =
        MemoryNamespace::try_new("foreign-tenant", "fixture-principal", "workspace")
            .expect("valid foreign namespace");
    repository
        .apply(MemoryChangeSet::new(
            "seed-foreign-multilingual-node",
            foreign_namespace.clone(),
            fixture_time(),
            vec![MemoryOperation::Create {
                node: draft(&fixture.foreign_node, &foreign_namespace),
            }],
        ))
        .await
        .expect("seed foreign multilingual node");
    (repository, namespace)
}

#[tokio::test]
async fn real_sessions_meet_versioned_multilingual_lexical_gate() {
    let fixture: Fixture = serde_json::from_str(FIXTURE).expect("valid multilingual fixture");
    assert_eq!(fixture.schema_version, 1);
    assert_eq!(
        fixture.retrieval_profile,
        DURABLE_MEMORY_RETRIEVAL_PROFILE_V1
    );
    let (repository, namespace) = seed(&fixture).await;
    let policy = DurableMemoryRecallPolicy::try_new(
        fixture.policy.max_results,
        fixture.policy.min_lexical_score,
    )
    .expect("valid multilingual recall policy")
    .try_with_related_lookups(fixture.policy.max_related_lookups)
    .expect("valid multilingual relation bound");
    let durable =
        DurableMemorySession::active_recall(repository.clone(), namespace.clone(), policy);
    assert_eq!(
        durable.binding().retrieval_profile(),
        fixture.retrieval_profile
    );

    let mut recalled = 0usize;
    let mut reciprocal_rank = 0.0;
    for query in &fixture.queries {
        let ids = durable
            .preview_recall(&query.query)
            .await
            .expect("preview multilingual recall")
            .hits
            .into_iter()
            .map(|hit| hit.node_id)
            .collect::<Vec<_>>();
        let rank = ids
            .iter()
            .position(|id| id == &query.relevant_node_id)
            .expect("relevant multilingual memory must be recalled");
        assert_eq!(rank, 0, "{} ranking changed: {ids:?}", query.id);
        if rank < 3 {
            recalled += 1;
        }
        reciprocal_rank += 1.0 / (rank + 1) as f64;
    }
    let recall_at_3 = recalled as f64 / fixture.queries.len() as f64;
    let mean_reciprocal_rank = reciprocal_rank / fixture.queries.len() as f64;

    let mut negative_queries_with_hits = 0usize;
    for query in &fixture.negative_queries {
        let hits = durable
            .preview_recall(&query.query)
            .await
            .expect("preview negative multilingual recall")
            .hits;
        if !hits.is_empty() {
            negative_queries_with_hits += 1;
        }
        assert!(
            hits.is_empty(),
            "{} unexpectedly returned {hits:?}",
            query.id
        );
    }

    let client = Arc::new(InspectingClient::new(&fixture));
    let agent = Agent::from_config(offline_config())
        .await
        .expect("create multilingual evaluation agent");
    let workspace = tempfile::tempdir().expect("create multilingual workspace");
    let env = host_env();
    for query in &fixture.queries {
        let options = SessionOptions::new()
            .with_session_id(format!("multilingual-{}", query.id))
            .with_planning_mode(PlanningMode::Disabled)
            .with_memory(Arc::new(InMemoryStore::new()))
            .with_durable_memory(durable.clone())
            .with_host_env(env.clone())
            .with_llm_client(client.clone());
        let session = agent
            .session_async(workspace.path().display().to_string(), Some(options))
            .await
            .expect("create multilingual evaluation session");
        let result = session
            .send(&query.query, None)
            .await
            .expect("run multilingual evaluation session");
        assert_eq!(result.text, format!("PASS:{}", query.id));
        session.close().await;
    }
    agent.close().await;

    let observations = client.observations();
    assert_eq!(observations.len(), fixture.queries.len());
    for observation in &observations {
        assert!(
            observation.target_visible,
            "{} target was absent from the real model context",
            observation.query_id
        );
        assert!(
            !observation.forbidden_visible,
            "{} leaked candidate or foreign memory",
            observation.query_id
        );
        assert!(
            observation.context_nodes <= fixture.thresholds.maximum_context_nodes_per_query,
            "{} injected {} nodes",
            observation.query_id,
            observation.context_nodes
        );
    }

    let mut admissions = 0u64;
    for node in &fixture.nodes {
        let usage = repository
            .usage_summary(&namespace, &node.id)
            .await
            .expect("read multilingual usage summary");
        admissions += usage.admissions;
        assert_eq!(usage.uses, 0, "evaluation must not fabricate use events");
    }
    let maximum_context_nodes = observations
        .iter()
        .map(|observation| observation.context_nodes)
        .max()
        .unwrap_or(0);
    let leaks = observations
        .iter()
        .filter(|observation| observation.forbidden_visible)
        .count();

    assert!(recall_at_3 >= fixture.thresholds.minimum_recall_at_3);
    assert!(mean_reciprocal_rank >= fixture.thresholds.minimum_mean_reciprocal_rank);
    assert!(
        observations.len()
            <= fixture.queries.len() * fixture.thresholds.maximum_model_calls_per_query
    );
    assert_eq!(admissions, fixture.thresholds.expected_admissions);

    println!(
        "A3S_DURABLE_MEMORY_MULTILINGUAL_EVAL={}",
        serde_json::to_string(&Report {
            schema_version: fixture.schema_version,
            retrieval_profile: DURABLE_MEMORY_RETRIEVAL_PROFILE_V1,
            queries: fixture.queries.len(),
            recall_at_3,
            mean_reciprocal_rank,
            model_calls: observations.len(),
            admissions,
            maximum_context_nodes,
            negative_queries_with_hits,
            candidate_or_foreign_leaks: leaks,
        })
        .expect("serialize multilingual report")
    );
}