use sim_codec_bridge::{
BridgeBook, BridgePacket, BridgePart, BridgeWeavePayload, BridgeWeaveRow, WeavePart,
stamp_packet_cid,
};
use sim_kernel::{
Consistency, Cx, Error, EvalFabric, EvalMode, EvalReply, EvalRequest, Expr, Result, Symbol,
};
use sim_lib_agent_runner_core::{
ModelRequest, ModelResponse, OUTPUT_GRAMMAR_EXTRA, OUTPUT_GRAMMAR_REQUIRED_EXTRA,
RETURN_CODEC_EXTRA, RETURN_SHAPE_EXTRA, terminal_model_content,
};
use sim_value::{access::field, build::entry};
use crate::frontier::FrontierMenu;
use crate::loom_validate::{next_frontier_menu, validate_weave};
use crate::report::{BridgeObligation, BridgeReport};
use crate::rx::{effective_caps, rx_check};
pub fn validate_woven_row(
cx: &mut Cx,
packet: &BridgePacket,
accepted_rows: &[BridgeWeaveRow],
row: BridgeWeaveRow,
) -> Result<BridgeReport> {
let rows = accepted_rows
.iter()
.cloned()
.chain(std::iter::once(row))
.collect();
let weave = BridgeWeavePayload::new(rows);
validate_weave(cx, &packet_with_weave(packet, &weave)?, &weave)
}
pub fn weave_row_by_row(
cx: &mut Cx,
target: &dyn EvalFabric,
packet: BridgePacket,
weave: &WeavePart,
) -> Result<BridgePacket> {
if weave.rows.is_empty() {
return Err(Error::Eval(
"LOOM woven mode requires at least one row budget entry".to_owned(),
));
}
require_weave_part(&packet)?;
let mut accepted_rows = Vec::new();
for row_index in 0..weave.rows.len() {
let row = request_woven_row(cx, target, &packet, &accepted_rows, row_index, &[])?;
let report = validate_woven_row(cx, &packet, &accepted_rows, row.clone())?;
if report.accepted() {
accepted_rows.push(row);
continue;
}
let replacement = request_woven_row(
cx,
target,
&packet,
&accepted_rows,
row_index,
&report.obligations,
)?;
let replacement_report =
validate_woven_row(cx, &packet, &accepted_rows, replacement.clone())?;
if !replacement_report.accepted() {
return Err(Error::Eval(format!(
"LOOM woven repair failed at loom/rows/{row_index}: {:?}",
replacement_report.obligations
)));
}
accepted_rows.push(replacement);
}
let mut completed = packet_with_weave(&packet, &BridgeWeavePayload::new(accepted_rows))?;
completed.header.cid = None;
let completed = stamp_packet_cid(&completed)?;
let book = BridgeBook::standard();
let report = rx_check(cx, &book, &completed, None)?;
if !report.accepted() {
return Err(Error::Eval(format!(
"LOOM woven packet failed final check: {:?}",
report.obligations
)));
}
Ok(completed)
}
fn request_woven_row(
cx: &mut Cx,
target: &dyn EvalFabric,
packet: &BridgePacket,
accepted_rows: &[BridgeWeaveRow],
row_index: usize,
obligations: &[BridgeObligation],
) -> Result<BridgeWeaveRow> {
let request = woven_row_request(cx, packet, accepted_rows, row_index, obligations)?;
let caps = effective_caps(cx, packet)?;
let reply = cx.with_capabilities(caps, |cx| target.realize(cx, request))?;
decode_row_reply(cx, reply)
}
fn woven_row_request(
cx: &mut Cx,
packet: &BridgePacket,
accepted_rows: &[BridgeWeaveRow],
row_index: usize,
obligations: &[BridgeObligation],
) -> Result<EvalRequest> {
let partial_weave = BridgeWeavePayload::new(accepted_rows.to_vec());
let partial_packet = packet_with_optional_weave(packet, Some(&partial_weave))?;
let menu = next_frontier_menu(cx, &partial_packet)?;
let role_refs = role_ref_menu(packet, accepted_rows);
let mut model = ModelRequest::new(
Expr::Map(vec![
entry(
"mode",
Expr::Symbol(Symbol::qualified("bridge", "loom-woven-row")),
),
entry("row-path", row_path(row_index)),
entry("head-menu", menu.heads.clone()),
entry("role-ref-menu", string_vector(&role_refs)),
entry("slot-menu", slot_menu_expr(&menu)),
entry("obligations", obligations_expr(obligations)),
]),
Vec::new(),
);
model.extra.push(entry(
"bridge-mode",
Expr::Symbol(Symbol::qualified("bridge", "loom-woven-row")),
));
model
.extra
.push(entry("bridge-row-path", row_path(row_index)));
model
.extra
.push(entry("bridge-frontier-heads", menu.heads.clone()));
model
.extra
.push(entry("bridge-frontier-slots", slot_menu_expr(&menu)));
model
.extra
.push(entry("bridge-role-refs", string_vector(&role_refs)));
model.extra.push(entry(
"bridge-frontier-grammar",
Expr::String(menu.grammar.clone()),
));
if !obligations.is_empty() {
model
.extra
.push(entry("bridge-obligations", obligations_expr(obligations)));
}
model.extra.push(entry(
RETURN_CODEC_EXTRA,
Expr::Symbol(Symbol::qualified("codec", "bridge")),
));
model
.extra
.push(entry(RETURN_SHAPE_EXTRA, row_shape_expr(&menu, &role_refs)));
model.extra.push(entry(
OUTPUT_GRAMMAR_EXTRA,
Expr::String(row_grammar(&menu, &role_refs)),
));
model
.extra
.push(entry(OUTPUT_GRAMMAR_REQUIRED_EXTRA, Expr::Bool(true)));
Ok(EvalRequest {
expr: Expr::from(model),
result_shape: None,
required_capabilities: effective_caps(cx, packet)?.iter().cloned().collect(),
deadline: None,
consistency: Consistency::default(),
mode: EvalMode::default(),
answer_limit: None,
stream_buffer: None,
stream: false,
trace: false,
})
}
fn decode_row_reply(cx: &mut Cx, reply: EvalReply) -> Result<BridgeWeaveRow> {
let response = ModelResponse::try_from(reply.value.object().as_expr(cx)?)?;
let content = terminal_model_content(&response)?;
BridgeWeaveRow::from_expr(field(content, "row").unwrap_or(content))
}
fn packet_with_weave(packet: &BridgePacket, weave: &BridgeWeavePayload) -> Result<BridgePacket> {
packet_with_optional_weave(packet, Some(weave))
}
fn packet_with_optional_weave(
packet: &BridgePacket,
weave: Option<&BridgeWeavePayload>,
) -> Result<BridgePacket> {
let mut packet = packet.clone();
let index = require_weave_part(&packet)?;
if let Some(weave) = weave {
packet.body[index].payload = weave.to_expr();
}
packet.header.cid = None;
Ok(packet)
}
fn require_weave_part(packet: &BridgePacket) -> Result<usize> {
packet
.body
.iter()
.position(is_weave_part)
.ok_or_else(|| Error::Eval("LOOM woven mode requires a bridge/Weave part".to_owned()))
}
fn is_weave_part(part: &BridgePart) -> bool {
part.kind == Symbol::qualified("bridge", "Weave")
}
fn role_ref_menu(packet: &BridgePacket, accepted_rows: &[BridgeWeaveRow]) -> Vec<String> {
let mut refs = packet
.body
.iter()
.filter(|part| !is_weave_part(part))
.map(|part| part.id.as_qualified_str())
.chain(packet.header.context.iter().map(Symbol::as_qualified_str))
.chain(accepted_rows.iter().map(|row| row.slot.clone()))
.collect::<Vec<_>>();
refs.sort();
refs.dedup();
refs
}
fn row_shape_expr(menu: &FrontierMenu, role_refs: &[String]) -> Expr {
Expr::Map(vec![
entry(
"shape",
Expr::Symbol(Symbol::qualified("bridge", "WeaveRow")),
),
entry("slot", Expr::Symbol(Symbol::qualified("core", "String"))),
entry("head", menu.heads.clone()),
entry("roles", string_vector(role_refs)),
])
}
fn row_grammar(menu: &FrontierMenu, role_refs: &[String]) -> String {
format!(
"{{\"type\":\"object\",\"required\":[\"slot\",\"head\",\"roles\"],\"properties\":{{\"slot\":{{\"type\":\"string\"}},\"head\":{},\"roles\":{{\"type\":\"object\",\"additionalProperties\":{{\"enum\":{}}}}}}}}}",
menu.grammar,
json_string_array(role_refs)
)
}
fn slot_menu_expr(menu: &FrontierMenu) -> Expr {
Expr::Vector(
menu.slots
.iter()
.map(|(slot, shape)| {
Expr::Map(vec![
entry("slot", Expr::String(slot.clone())),
entry("shape", shape.clone()),
])
})
.collect(),
)
}
fn obligations_expr(obligations: &[BridgeObligation]) -> Expr {
Expr::Vector(obligations.iter().map(BridgeObligation::to_expr).collect())
}
fn string_vector(items: &[String]) -> Expr {
Expr::Vector(items.iter().cloned().map(Expr::String).collect())
}
fn row_path(row_index: usize) -> Expr {
Expr::String(format!("loom/rows/{row_index}"))
}
fn json_string_array(items: &[String]) -> String {
let values = items
.iter()
.map(|item| format!("\"{}\"", json_escape(item)))
.collect::<Vec<_>>();
format!("[{}]", values.join(","))
}
fn json_escape(text: &str) -> String {
let mut escaped = String::new();
for ch in text.chars() {
match ch {
'"' => escaped.push_str("\\\""),
'\\' => escaped.push_str("\\\\"),
'\n' => escaped.push_str("\\n"),
'\r' => escaped.push_str("\\r"),
'\t' => escaped.push_str("\\t"),
other => escaped.push(other),
}
}
escaped
}