use crate::llm::tools;
use crate::value::{VmError, VmValue};
pub(super) fn parse_error_carrier_feedback(
tool_name: &str,
parse_error: &str,
) -> (String, &'static str) {
if parse_error_is_truncation(parse_error) {
(
format!(
"Tool '{tool_name}' arguments could NOT be parsed because the tool call was \
TRUNCATED mid-stream — the arguments JSON ended before it was complete. This \
is NOT a missing-parameter slip: you did author the arguments, but the \
response was cut off before they finished. Re-issue the call with shorter \
content, or split the change into several smaller calls so the arguments fit \
in one response."
),
"arguments_truncated",
)
} else {
(
format!(
"Tool '{tool_name}' arguments could NOT be parsed as valid JSON. Re-issue the \
call as one complete, well-formed JSON object with the required parameters."
),
"arguments_malformed",
)
}
}
pub(super) fn parse_error_is_truncation(parse_error: &str) -> bool {
parse_error.contains("EOF while parsing") || parse_error.contains("unexpected end of input")
}
pub(super) fn append_empty_required_arg_diagnostics(
parsed: &mut serde_json::Value,
calls: &[serde_json::Value],
registry: Option<&VmValue>,
) -> Result<(), VmError> {
let schemas = tools::collect_tool_schemas(registry, None);
let diagnostics = calls
.iter()
.filter_map(|call| {
let name = call.get("name")?.as_str()?;
let arguments = call
.get("arguments")
.cloned()
.unwrap_or(serde_json::Value::Null);
arguments
.as_object()
.filter(|object| object.is_empty())
.and_then(|_| tools::validate_tool_args(name, &arguments, &schemas).err())
})
.collect::<Vec<_>>();
let errors = parsed
.get_mut("tool_parse_errors")
.and_then(serde_json::Value::as_array_mut)
.ok_or_else(|| {
VmError::Runtime(
"__host_agent_parse_tool_calls: std/llm/tool_parse returned no tool_parse_errors list"
.to_string(),
)
})?;
for message in diagnostics {
if !errors
.iter()
.any(|entry| entry.as_str() == Some(message.as_str()))
{
errors.push(serde_json::Value::String(message));
}
}
Ok(())
}