use super::display;
use super::tools;
use super::CompletedSpillRenderer;
use std::io;
pub(super) fn is_tools_unsupported_error(e: &anyhow::Error) -> bool {
let s = e.to_string().to_lowercase();
s.contains("does not support tools") || s.contains("not support tools")
}
pub(super) fn is_ollama_tool_xml_error(e: &anyhow::Error) -> bool {
let s = e.to_string().to_lowercase();
s.contains("ollama") && s.contains("xml syntax error")
}
pub(super) fn ollama_tool_xml_retry_nudge() -> &'static str {
"The previous assistant turn failed inside Ollama's XML tool-call parser. \
Keep using tools, but emit exactly one valid native tool call with well-formed \
arguments now. Do not answer in prose or wrap the call in explanatory XML."
}
pub(super) fn is_cancelled(cancel: Option<&std::sync::atomic::AtomicBool>) -> bool {
cancel.is_some_and(|c| c.load(std::sync::atomic::Ordering::Relaxed))
}
pub(super) fn dismiss_completed_spill(
renderer: &Option<std::sync::Arc<dyn CompletedSpillRenderer>>,
) {
if let Some(renderer) = renderer {
renderer.erase();
}
}
async fn cancelled(cancel: Option<&std::sync::atomic::AtomicBool>) {
match cancel {
None => std::future::pending().await,
Some(flag) => {
while !flag.load(std::sync::atomic::Ordering::Relaxed) {
tokio::time::sleep(std::time::Duration::from_millis(15)).await;
}
}
}
}
pub(super) async fn cancellable<F: std::future::Future>(
cancel: Option<&std::sync::atomic::AtomicBool>,
fut: F,
) -> Option<F::Output> {
tokio::select! {
biased;
_ = cancelled(cancel) => None,
v = fut => Some(v),
}
}
pub(super) fn present_synthetic_tool_result<W: std::io::Write>(
display: &mut display::ToolDisplay<W>,
name: &str,
args: &serde_json::Value,
workspace: &std::path::Path,
result: &str,
) {
let (name, detail) =
tools::tool_presentation(name, args, workspace, &crate::caveats::Scope::All);
display.call(&name, &detail);
display.result(result);
}
pub(super) fn print_synthetic_tool_result(
name: &str,
args: &serde_json::Value,
workspace: &str,
result: &str,
color: bool,
completed_spill_renderer: &Option<std::sync::Arc<dyn CompletedSpillRenderer>>,
) {
dismiss_completed_spill(completed_spill_renderer);
let mut tool_display = display::ToolDisplay::new(
io::stdout(),
color,
display::term_cols(),
display::spill_lines(),
display::spill_summary(),
);
present_synthetic_tool_result(
&mut tool_display,
name,
args,
std::path::Path::new(workspace),
result,
);
}
pub(super) fn legacy_caps(animate: bool) -> crate::tty::LineCaps {
if animate {
crate::tty::LineCaps::Own
} else {
crate::tty::LineCaps::None
}
}
pub(super) fn announce_tool_activity(name: &str) {
if crate::lifecycle::observed() {
crate::lifecycle::emit(crate::lifecycle::LifecycleEvent::ToolActivity {
tool: name.to_string(),
});
}
}