use std::sync::Mutex;
use sim_codec_bridge::{
BridgeBook, BridgeCallPayload, BridgeHeader, BridgePacket, BridgePart, BridgeProvenance,
encode_bridge_text, packet_content_id, packet_to_expr, stamp_packet_cid,
};
use sim_kernel::{
ContentId, Cx, Error, EvalFabric, EvalReply, EvalRequest, Expr, Result, Symbol,
testing::bare_cx,
};
use sim_lib_agent_runner_core::ModelResponse;
use sim_value::{access::field, build::entry};
use crate::{
CompiledIntent, IntentLibrary, IntentStatus, LiftOptions, PromotePolicy,
forge_resolve_with_options, normalize_prose,
};
struct ScriptedLiftFabric {
responses: Mutex<Vec<Expr>>,
requests: Mutex<Vec<Expr>>,
}
impl ScriptedLiftFabric {
fn new(responses: Vec<Expr>) -> Self {
Self {
responses: Mutex::new(responses),
requests: Mutex::new(Vec::new()),
}
}
fn requests(&self) -> Vec<Expr> {
self.requests.lock().unwrap().clone()
}
}
impl EvalFabric for ScriptedLiftFabric {
fn realize(&self, cx: &mut Cx, request: EvalRequest) -> Result<EvalReply> {
self.requests.lock().unwrap().push(request.expr.clone());
let parent_cid = bridge_cid_from_request(&request)?;
let payload = {
let mut responses = self.responses.lock().unwrap();
if responses.is_empty() {
return Err(Error::Eval(
"scripted resolve fabric is exhausted".to_owned(),
));
}
responses.remove(0)
};
let reply = stamp_packet_cid(&reply_packet(&parent_cid, payload))?;
let response = ModelResponse::new(
Symbol::qualified("runner", "forge-resolve-fixture"),
"forge-resolve-fixture",
vec![text_content(encode_bridge_text(
&reply,
&BridgeBook::standard(),
)?)],
Symbol::new("stop"),
);
Ok(EvalReply {
value: cx.factory().expr(Expr::from(response))?,
diagnostics: Vec::new(),
trace: None,
})
}
}
#[derive(Default)]
struct FailingLiftFabric {
requests: Mutex<Vec<Expr>>,
}
impl FailingLiftFabric {
fn request_count(&self) -> usize {
self.requests.lock().unwrap().len()
}
}
impl EvalFabric for FailingLiftFabric {
fn realize(&self, _cx: &mut Cx, request: EvalRequest) -> Result<EvalReply> {
self.requests.lock().unwrap().push(request.expr);
Err(Error::Eval(
"golden resolve hit must not call lift fabric".to_owned(),
))
}
}
fn bridge_cid_from_request(request: &EvalRequest) -> Result<String> {
match field(&request.expr, "bridge-cid") {
Some(Expr::String(cid)) => Ok(cid.clone()),
_ => Err(Error::Eval(
"forge resolve test request is missing bridge-cid".to_owned(),
)),
}
}
fn text_content(text: String) -> Expr {
Expr::Map(vec![
entry("type", Expr::Symbol(Symbol::new("text"))),
entry("text", Expr::String(text)),
])
}
fn reply_packet(parent_cid: &str, payload: Expr) -> BridgePacket {
BridgePacket {
header: BridgeHeader {
cid: None,
move_kind: Symbol::new("reply"),
from: "model:forge-lift".to_owned(),
to: vec!["sim".to_owned()],
role: Symbol::new("implementer"),
parents: vec![parent_cid.to_owned()],
task: Symbol::new("A1"),
output: Symbol::new("A1"),
ceiling: Vec::new(),
context: Vec::new(),
provenance: BridgeProvenance::default(),
},
body: vec![BridgePart {
id: Symbol::new("A1"),
kind: Symbol::qualified("bridge", "Return"),
payload,
}],
warrant: None,
}
}
fn candidate_packet(return_shape: Expr) -> BridgePacket {
BridgePacket {
header: BridgeHeader {
cid: None,
move_kind: Symbol::new("request"),
from: "sim".to_owned(),
to: vec!["model:worker".to_owned()],
role: Symbol::new("implementer"),
parents: Vec::new(),
task: Symbol::new("C1"),
output: Symbol::new("O1"),
ceiling: Vec::new(),
context: Vec::new(),
provenance: BridgeProvenance::default(),
},
body: vec![
BridgePart {
id: Symbol::new("C1"),
kind: Symbol::qualified("bridge", "Call"),
payload: BridgeCallPayload::new(Symbol::qualified("forge", "answer")).to_expr(),
},
BridgePart {
id: Symbol::new("O1"),
kind: Symbol::qualified("bridge", "Return"),
payload: Expr::Map(vec![
entry("codec", Expr::Symbol(Symbol::qualified("codec", "json"))),
entry("shape", return_shape),
]),
},
],
warrant: None,
}
}
fn candidate_payload(return_shape: Expr) -> Expr {
packet_to_expr(&candidate_packet(return_shape))
}
fn content_id(byte: u8) -> ContentId {
ContentId::from_bytes(Symbol::qualified("core", "sha256"), [byte; 32])
}
fn lift_options() -> LiftOptions {
LiftOptions {
name: Symbol::qualified("forge", "summarize"),
max_repairs: 0,
}
}
fn golden_intent(source: ContentId, packet: ContentId, version: u32) -> CompiledIntent {
CompiledIntent {
name: Symbol::qualified("forge", "summarize"),
version,
source,
packet,
verifiers: Vec::new(),
probes: Vec::new(),
status: IntentStatus::Golden,
compiler_card: None,
approval: Some(content_id(200)),
}
}
#[test]
fn normalize_prose_collapses_ascii_whitespace_and_preserves_case() {
let (normalized, source) = normalize_prose(" Summarize\tFoo\nBar ").unwrap();
let (same, same_source) = normalize_prose("Summarize Foo Bar").unwrap();
let (different_case, different_source) = normalize_prose("summarize Foo Bar").unwrap();
assert_eq!(normalized, "Summarize Foo Bar");
assert_eq!(same, normalized);
assert_eq!(same_source, source);
assert_eq!(different_case, "summarize Foo Bar");
assert_ne!(different_source, source);
}
#[test]
fn golden_artifact_hit_skips_lift_runner() {
let mut cx = bare_cx();
let (_, source) = normalize_prose("summarize the transcript").unwrap();
let golden = golden_intent(source, content_id(40), 3);
let mut library = IntentLibrary::new();
library.store(golden.clone()).unwrap();
let fabric = FailingLiftFabric::default();
let resolved = forge_resolve_with_options(
&mut library,
&lift_options(),
&mut cx,
&fabric,
" summarize \n the\ttranscript ",
PromotePolicy::KeepCandidate,
)
.unwrap();
assert_eq!(resolved, golden);
assert_eq!(fabric.request_count(), 0);
}
#[test]
fn relift_does_not_clobber_golden() {
let mut cx = bare_cx();
let (_, source) = normalize_prose("summarize the transcript").unwrap();
let golden = golden_intent(source, content_id(41), 1);
let mut library = IntentLibrary::new();
library.store(golden.clone()).unwrap();
let fabric = ScriptedLiftFabric::new(vec![candidate_payload(Expr::Symbol(Symbol::qualified(
"core", "String",
)))]);
let resolved = forge_resolve_with_options(
&mut library,
&lift_options(),
&mut cx,
&fabric,
"summarize the transcript",
PromotePolicy::KeepCandidate,
)
.unwrap();
assert_eq!(resolved, golden);
assert_eq!(fabric.requests().len(), 0);
assert_eq!(
library.fetch(&Symbol::qualified("forge", "summarize"), 1),
Some(&golden)
);
}
#[test]
fn changed_prose_normal_form_recompiles() {
let mut cx = bare_cx();
let (_, old_source) = normalize_prose("summarize the transcript").unwrap();
let (_, new_source) = normalize_prose("summarize the transcript now").unwrap();
let golden = golden_intent(old_source.clone(), content_id(42), 1);
let candidate = stamp_packet_cid(&candidate_packet(Expr::Symbol(Symbol::qualified(
"core", "String",
))))
.unwrap();
let mut library = IntentLibrary::new();
library.store(golden.clone()).unwrap();
let fabric = ScriptedLiftFabric::new(vec![packet_to_expr(&candidate)]);
let resolved = forge_resolve_with_options(
&mut library,
&lift_options(),
&mut cx,
&fabric,
"summarize the transcript now",
PromotePolicy::KeepCandidate,
)
.unwrap();
assert_eq!(fabric.requests().len(), 1);
assert_eq!(resolved.status, IntentStatus::Candidate);
assert_eq!(resolved.source, new_source);
assert_eq!(resolved.packet, packet_content_id(&candidate).unwrap());
assert_eq!(resolved.version, 2);
assert_eq!(library.by_source(&old_source), vec![&golden]);
assert_eq!(library.by_source(&new_source), vec![&resolved]);
}
#[test]
fn rx_check_only_leaves_candidate() {
let mut cx = bare_cx();
let (_, source) = normalize_prose("summarize the transcript").unwrap();
let candidate = stamp_packet_cid(&candidate_packet(Expr::Symbol(Symbol::qualified(
"core", "String",
))))
.unwrap();
let mut library = IntentLibrary::new();
let fabric = ScriptedLiftFabric::new(vec![packet_to_expr(&candidate)]);
let resolved = forge_resolve_with_options(
&mut library,
&lift_options(),
&mut cx,
&fabric,
"summarize the transcript",
PromotePolicy::AutoVerifiedOnProbePass,
)
.unwrap();
assert_eq!(resolved.status, IntentStatus::Candidate);
assert_eq!(resolved.approval, None);
assert_eq!(library.by_source(&source), vec![&resolved]);
}