Skip to main content

bamboo_engine/session_app/execute/
validation.rs

1//! Pre-execution validation helpers.
2
3use bamboo_domain::Session;
4
5/// Validate images after resolving the provider and exact executing model.
6/// Explicit support bypasses the independent legacy fallback; missing support
7/// retains its opt-in policy. Explicit false uses transient ViewImage references;
8/// the HTTP host separately validates the explicit auxiliary target configuration.
9pub fn validate_image_fallback_for_session(
10    session: &Session,
11    image_fallback: Option<&crate::ImageFallbackConfig>,
12    _model_name: &str,
13    vision_override: Option<bool>,
14) -> Result<(), String> {
15    use crate::ImageFallbackMode;
16
17    if vision_override.is_some() {
18        return Ok(());
19    }
20    let images_seen = session
21        .messages
22        .iter()
23        .filter_map(|message| message.content_parts.as_ref())
24        .flat_map(|parts| parts.iter())
25        .filter(|part| matches!(part, bamboo_agent_core::MessagePart::ImageUrl { .. }))
26        .count();
27    if matches!(
28        image_fallback,
29        Some(crate::ImageFallbackConfig {
30            mode: ImageFallbackMode::Error,
31            ..
32        })
33    ) && images_seen > 0
34    {
35        return Err(format!(
36            "This server does not currently support image inputs (found {images_seen} image part(s)). \
37             Configure hooks.image_fallback.mode='placeholder' or 'ocr' to degrade gracefully."
38        ));
39    }
40
41    Ok(())
42}
43
44#[cfg(test)]
45mod image_reference_tests {
46    use super::*;
47    use bamboo_domain::{ImageUrlRef, Message, MessagePart};
48
49    #[test]
50    fn explicit_nonvision_new_upload_and_history_followup_pass_without_rewriting_messages() {
51        for followup in [false, true] {
52            let mut session = Session::new("image-reference-preflight", "text-only");
53            session.add_message(Message::user_with_parts(
54                "image",
55                vec![MessagePart::ImageUrl {
56                    image_url: ImageUrlRef {
57                        url: "bamboo-attachment://image-reference-preflight/image1".into(),
58                        detail: None,
59                    },
60                }],
61            ));
62            if followup {
63                session.add_message(Message::user("inspect the earlier image"));
64            }
65            let before = serde_json::to_vec(&session).unwrap();
66            validate_image_fallback_for_session(
67                &session,
68                Some(&crate::ImageFallbackConfig {
69                    mode: crate::ImageFallbackMode::Error,
70                    vision_model: None,
71                }),
72                "text-only",
73                Some(false),
74            )
75            .unwrap();
76            assert_eq!(serde_json::to_vec(&session).unwrap(), before);
77        }
78    }
79}