use crate::agent::completions::message::{Message, RichContent, RichContentPart};
use crate::functions::expression::InputSchema;
use crate::functions::VectorCompletionTask;
pub type ModalityFlags = [bool; 4];
const IMAGE: usize = 0;
const AUDIO: usize = 1;
const VIDEO: usize = 2;
const FILE: usize = 3;
pub fn collect_schema_modalities(schema: &InputSchema, flags: &mut ModalityFlags) {
match schema {
InputSchema::Image(_) => flags[IMAGE] = true,
InputSchema::Audio(_) => flags[AUDIO] = true,
InputSchema::Video(_) => flags[VIDEO] = true,
InputSchema::File(_) => flags[FILE] = true,
InputSchema::Object(obj) => {
for prop_schema in obj.properties.values() {
collect_schema_modalities(prop_schema, flags);
}
}
InputSchema::Array(arr) => {
collect_schema_modalities(&arr.items, flags);
}
InputSchema::AnyOf(any_of) => {
for option in &any_of.any_of {
collect_schema_modalities(option, flags);
}
}
InputSchema::String(_)
| InputSchema::Integer(_)
| InputSchema::Number(_)
| InputSchema::Boolean(_) => {}
}
}
pub fn collect_task_modalities(
task: &VectorCompletionTask,
flags: &mut ModalityFlags,
) {
for msg in &task.messages {
if let Some(content) = message_rich_content(msg) {
collect_rich_content_modalities(content, flags);
}
}
for response in &task.responses {
collect_rich_content_modalities(response, flags);
}
}
pub fn check_modality_coverage(
schema_flags: &ModalityFlags,
task_flags: &ModalityFlags,
code: &str,
) -> Result<(), String> {
const NAMES: [&str; 4] = ["image", "audio", "video", "file"];
let mut missing = Vec::new();
for i in 0..4 {
if schema_flags[i] && !task_flags[i] {
missing.push(NAMES[i]);
}
}
if missing.is_empty() {
Ok(())
} else {
Err(format!(
"{}: input_schema declares multimodal type(s) [{}] but no compiled task \
contains content parts of that type — tasks must use multimodal input, \
not convert it to text with str()",
code,
missing.join(", "),
))
}
}
fn message_rich_content(msg: &Message) -> Option<&RichContent> {
match msg {
Message::User(u) => Some(&u.content),
Message::Tool(t) => Some(&t.content),
Message::Assistant(a) => a.content.as_ref(),
Message::Developer(_) | Message::System(_) => None,
}
}
fn collect_rich_content_modalities(
content: &RichContent,
flags: &mut ModalityFlags,
) {
if let RichContent::Parts(parts) = content {
for part in parts {
match part {
RichContentPart::ImageUrl { .. } => flags[IMAGE] = true,
RichContentPart::InputAudio { .. } => flags[AUDIO] = true,
RichContentPart::InputVideo { .. } | RichContentPart::VideoUrl { .. } => {
flags[VIDEO] = true
}
RichContentPart::File { .. } => flags[FILE] = true,
RichContentPart::Text { .. } => {}
}
}
}
}