sim-lib-agent 0.1.2

Agent runtime surfaces for SIM.
Documentation
use super::support::{eval_cx, flatten_text};
use crate::{
    FakeAsrFixture, FakeAsrRunner, FakeAsrSegment, FakeSensorFrame, FakeSensorStream,
    FakeVisionFixture, FakeVisionRunner, ModelRequest, ModelRunner,
};
use sim_codec_chat::validate_chat_transcript;
use sim_kernel::{Expr, Symbol};

#[test]
fn fake_vision_runner_maps_synthetic_image_to_caption_and_fields() {
    let runner = FakeVisionRunner::new("fake-vision/model")
        .with_fixture(
            "fixture:image:dashboard",
            FakeVisionFixture::new("dashboard shows green deployment status")
                .with_field("status", "green")
                .with_field("owner", "ops"),
        )
        .unwrap();
    let mut cx = eval_cx();

    let response = runner
        .infer(
            &mut cx,
            ModelRequest::new(
                Expr::Map(vec![(
                    Expr::Symbol(Symbol::new("image-ref")),
                    Expr::String("fixture:image:dashboard".to_owned()),
                )]),
                Vec::new(),
            ),
        )
        .unwrap();

    let expr: Expr = response.clone().into();
    validate_chat_transcript(&expr).unwrap();
    let text = flatten_text(&expr);
    assert!(text.contains("green deployment status"));
    assert!(text.contains("owner"));
    assert_eq!(
        runner
            .card()
            .extra
            .iter()
            .find(|(key, _)| *key == Expr::Symbol(Symbol::new("modalities-in")))
            .map(|(_, value)| value),
        Some(&Expr::List(vec![Expr::Symbol(Symbol::new("image"))]))
    );
}

#[test]
fn fake_asr_runner_maps_synthetic_audio_to_transcript_segments() {
    let runner = FakeAsrRunner::new("fake-asr/model")
        .with_fixture(
            "fixture:audio:briefing",
            FakeAsrFixture::new("system nominal", "en-US")
                .with_segment(FakeAsrSegment::new(0, 500, "system").unwrap())
                .with_segment(FakeAsrSegment::new(500, 900, "nominal").unwrap()),
        )
        .unwrap();
    let mut cx = eval_cx();

    let response = runner
        .infer(
            &mut cx,
            ModelRequest::new(
                Expr::Map(vec![(
                    Expr::Symbol(Symbol::new("audio-ref")),
                    Expr::String("fixture:audio:briefing".to_owned()),
                )]),
                Vec::new(),
            ),
        )
        .unwrap();

    assert!(response.content.iter().any(|part| {
        matches!(part, Expr::Map(entries) if entries.iter().any(|(key, value)| {
            *key == Expr::Symbol(Symbol::new("locale")) && *value == Expr::String("en-US".to_owned())
        }))
    }));
    let expr: Expr = response.into();
    validate_chat_transcript(&expr).unwrap();
    let text = flatten_text(&expr);
    assert!(text.contains("system nominal"));
    assert!(text.contains("500"));
}

#[test]
fn fake_sensor_stream_replays_a_fixed_frame_sequence() {
    let frames = vec![
        FakeSensorFrame::new(0, vec![("distance-cm".to_owned(), 42.0)]).unwrap(),
        FakeSensorFrame::new(1, vec![("distance-cm".to_owned(), 40.5)]).unwrap(),
    ];
    let mut stream = FakeSensorStream::new(frames.clone()).unwrap();

    assert_eq!(stream.frames(), frames.as_slice());
    assert_eq!(stream.next_frame(), Some(frames[0].clone()));
    assert_eq!(stream.next_frame(), Some(frames[1].clone()));
    assert_eq!(stream.next_frame(), None);
    stream.reset();
    assert_eq!(stream.next(), Some(frames[0].clone()));
    assert!(flatten_text(&frames[0].as_expr()).contains("distance-cm"));
}

#[test]
fn fake_multimodal_fixtures_reject_external_or_live_inputs() {
    assert!(
        FakeVisionRunner::new("fake-vision/model")
            .with_fixture(
                "https://example.invalid/image.png",
                FakeVisionFixture::new("external")
            )
            .is_err()
    );
    assert!(
        FakeAsrRunner::new("fake-asr/model")
            .with_fixture(
                "file:///tmp/audio.wav",
                FakeAsrFixture::new("external", "en-US")
            )
            .is_err()
    );
    assert!(FakeAsrSegment::new(20, 10, "bad").is_err());
    assert!(FakeSensorFrame::new(0, vec![("temperature".to_owned(), f64::NAN)]).is_err());

    let runner = FakeVisionRunner::new("fake-vision/model");
    let mut cx = eval_cx();
    let response = runner
        .infer(
            &mut cx,
            ModelRequest::new(
                Expr::String("https://example.invalid/image.png".to_owned()),
                vec![],
            ),
        )
        .unwrap();
    let expr: Expr = response.into();
    validate_chat_transcript(&expr).unwrap();
    assert!(flatten_text(&expr).contains("missing synthetic image reference"));
}