use super::*;
pub(super) fn refuse_dropped_text_tools(
options: &Option<crate::value::DictMap>,
tool_format: &str,
native_tools_on_wire: bool,
route_supports_native_tools: bool,
provider: &str,
model: &str,
) -> Result<(), VmError> {
let tools = options.as_ref().and_then(|o| o.get("tools"));
if tool_format == "native"
|| !tools_value_has_entries(tools)
|| native_tools_on_wire
|| opt_bool(options, "_tool_contract_rendered")
{
return Ok(());
}
Err(crate::llm::call::invalid_request_error(
format!(
"`tools` with tool_format `{tool_format}` needs the tool-call contract that \
only `agent_loop` renders; a direct call would send no tools to `{model}` \
(provider `{provider}`). Drive the tools through `agent_loop`, or pass \
`tool_format: \"native\"`{native_hint}.",
native_hint = if route_supports_native_tools {
""
} else {
" on a route that supports native tools"
},
),
provider,
model,
))
}
fn tools_value_has_entries(tools: Option<&VmValue>) -> bool {
match tools {
None | Some(VmValue::Nil) => false,
Some(VmValue::List(items)) => !items.is_empty(),
Some(VmValue::Dict(registry)) => match registry.get("tools") {
Some(VmValue::List(items)) => !items.is_empty(),
_ => true,
},
Some(_) => true,
}
}