use super::*;
use serde_json::Value;
pub(super) fn append_blocks(
blocks: &mut Vec<ToolBlock>,
input: &ToolCardInput<'_>,
role: ToolCardRole,
) {
let detail = input.detail;
let empty = Value::Null;
let params = detail.map_or(&empty, |detail| &detail.params);
let metadata = detail.map_or(&empty, |detail| &detail.metadata);
let mut rows = Vec::new();
let mut fact = |label: &str, value: Option<String>| {
rows.push(ResultRow {
label: label.into(),
value,
outcome: ResultOutcome::Unknown,
})
};
let text = |value: &Value| value.as_str().map(sanitized_single_line);
let number = |value: &Value| value.as_u64().map(|number| number.to_string());
let boolean = |value: &Value| value.as_bool().map(|value| value.to_string());
match role {
ToolCardRole::Web => {
fact(
"Web operation",
params["operation"]
.as_str()
.filter(|operation| matches!(*operation, "search" | "open"))
.map(str::to_owned),
);
fact("Web query", text(¶ms["query"]));
fact(
"Web destination",
text(¶ms["url"]).or_else(|| text(&metadata["url"])),
);
fact(
"Web reference",
text(¶ms["reference"]).or_else(|| text(&metadata["reference"])),
);
fact("Web result count", number(&metadata["results"]));
fact("Web output truncated", boolean(&metadata["truncated"]));
fact(
"Web preview truncated",
detail
.and_then(response_preview_limited)
.map(|limited| limited.to_string()),
);
}
ToolCardRole::ViewImage => {
fact(
"Image source",
text(&metadata["path"]).or_else(|| text(¶ms["path"])),
);
fact("Image provider", text(&metadata["provider"]));
fact("Image model", text(&metadata["model"]));
fact("Image bytes", number(&metadata["image_bytes"]));
fact("Image prompt characters", number(&metadata["prompt_chars"]));
fact("Image output truncated", boolean(&metadata["truncated"]));
fact(
"Image preview truncated",
detail
.and_then(response_preview_limited)
.map(|limited| limited.to_string()),
);
}
ToolCardRole::MagiControl => {
let operation = crate::output::control::operation(params);
fact("Control operation", operation.map(str::to_owned));
if operation == Some("context_usage") {
fact("Context used tokens", number(&metadata["current_tokens"]));
fact("Context maximum tokens", number(&metadata["max_tokens"]));
fact("Context used percent", number(&metadata["percentage"]));
} else if operation == Some("compact") {
fact(
"Compaction status",
detail
.and_then(|detail| {
crate::output::control::compaction_status(
params,
metadata,
detail.status,
)
})
.map(str::to_owned),
);
}
}
_ => return,
}
blocks.insert(0, ToolBlock::Results { rows });
}
pub(super) fn response_preview_limited(detail: &ToolActivityDetail) -> Option<bool> {
let lines = match detail.status {
ActivityStatus::Success => TOOL_PREVIEW_LINES,
ActivityStatus::Failed | ActivityStatus::Canceled => 1,
_ => return None,
};
Some(preview_is_limited(
&sanitize_display_text(&detail.output),
lines,
))
}