Skip to main content

sim_lib_bridge/
brief.rs

1use sim_codec_bridge::{
2    BridgeBook, BridgeFramePayload, BridgeHeader, BridgePacket, BridgePart, BridgeProvenance,
3    render_frame_part_with_prose,
4};
5use sim_kernel::{Datum, Error, Expr, Result, Symbol};
6use sim_lib_agent_runner_core::InjectionFence;
7use sim_value::build::entry;
8
9/// Builds a BRIEF request packet from one typed frame.
10pub fn bridge_brief(
11    to: &str,
12    frame: BridgeFramePayload,
13    return_shape: Expr,
14) -> Result<BridgePacket> {
15    BridgeBook::standard()
16        .frames
17        .validate_payload(&frame.to_expr())?;
18    Ok(BridgePacket {
19        header: BridgeHeader {
20            cid: None,
21            move_kind: Symbol::new("request"),
22            from: "sim".to_owned(),
23            to: vec![to.to_owned()],
24            role: Symbol::new("implementer"),
25            parents: Vec::new(),
26            task: Symbol::new("T1"),
27            output: Symbol::new("O1"),
28            ceiling: vec![Symbol::qualified("ai", "run")],
29            context: Vec::new(),
30            provenance: BridgeProvenance::default(),
31        },
32        body: vec![
33            BridgePart {
34                id: Symbol::new("T1"),
35                kind: Symbol::qualified("bridge", "Frame"),
36                payload: frame.to_expr(),
37            },
38            BridgePart {
39                id: Symbol::new("O1"),
40                kind: Symbol::qualified("bridge", "Return"),
41                payload: Expr::Map(vec![
42                    entry("codec", Expr::Symbol(Symbol::qualified("codec", "bridge"))),
43                    entry("shape", return_shape),
44                ]),
45            },
46        ],
47        warrant: None,
48    })
49}
50
51/// Renders every `bridge/Frame` part as a cited BRIEF sentence.
52pub fn render_brief_sentences(
53    book: &BridgeBook,
54    packet: &BridgePacket,
55) -> Result<Vec<(String, String)>> {
56    packet
57        .body
58        .iter()
59        .filter(|part| part.kind == Symbol::qualified("bridge", "Frame"))
60        .map(|part| {
61            let part_id = part.id.as_qualified_str().to_owned();
62            let rendered = render_frame_part_with_prose(book, part, |hole, value| {
63                render_prose_hole(&part.id, hole, value)
64            })?;
65            Ok((part_id, rendered))
66        })
67        .collect()
68}
69
70fn render_prose_hole(part: &Symbol, hole: &Symbol, value: &Expr) -> Result<String> {
71    let Expr::String(text) = value else {
72        return Err(Error::Eval(format!(
73            "BRIDGE prose hole {}.{} must be text",
74            part, hole
75        )));
76    };
77    let id = Datum::String(text.clone()).content_id()?;
78    let fence = InjectionFence::for_content(&id);
79    Ok(fence.wrap(
80        &format!("{}.{}", part.as_qualified_str(), hole.as_qualified_str()),
81        text,
82    ))
83}