af-agent-runtime 0.2.0

Recoverable Turn/Step loop, tool pipeline, retry and context compaction.
Documentation
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"))))
}