use af_agent_session::ContentBlock;
use af_llm::AssistantBlock;
use crate::RuntimeError;
pub(super) fn assistant_content(
fallback_text: Option<&str>,
blocks: Vec<AssistantBlock>,
) -> Result<(Vec<ContentBlock>, Option<String>), RuntimeError> {
if blocks.is_empty() {
let text = fallback_text.unwrap_or_default().to_string();
return if text.is_empty() {
Ok((Vec::new(), None))
} else {
Ok((vec![ContentBlock::Text { text: text.clone() }], Some(text)))
};
}
let mut content = Vec::with_capacity(blocks.len());
let mut text = Vec::new();
for block in blocks {
match block {
AssistantBlock::Text { text: value } if !value.is_empty() => {
text.push(value.clone());
content.push(ContentBlock::Text { text: value });
}
AssistantBlock::Resource {
resource_id,
media_type,
} if !resource_id.trim().is_empty() && !media_type.trim().is_empty() => {
content.push(ContentBlock::Resource {
resource_id,
media_type,
});
}
AssistantBlock::Data { slot, value } if !slot.trim().is_empty() => {
content.push(ContentBlock::Data { slot, value });
}
AssistantBlock::Citation {
resource_id,
label,
uri,
excerpt,
} if !resource_id.trim().is_empty()
&& !label.trim().is_empty()
&& !uri.trim().is_empty() =>
{
content.push(ContentBlock::Citation {
resource_id,
label,
uri,
excerpt,
});
}
_ => {
return Err(RuntimeError::Model(
"assistant output contains an invalid structured block".into(),
));
}
}
}
Ok((content, (!text.is_empty()).then(|| text.join("\n"))))
}