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
9pub 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
51pub 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}