use ferrox_models::grammar::json_schema::GrammarBuilder;
use ferrox_models::grammar::LazyTriggers;
use super::{block, parameters, trigger};
use crate::policy::parser::ToolCallFormat;
use crate::tool_grammar::{schema_refused, ToolSpec};
use crate::ApiError;
pub(super) fn json_root(
builder: &mut GrammarBuilder,
format: ToolCallFormat,
tools: &[ToolSpec<'_>],
array: bool,
) -> Result<(String, LazyTriggers), ApiError> {
let markers = format.markers();
let mut alternatives = Vec::with_capacity(tools.len());
for tool in tools {
let args = builder
.add_schema_value(&format!("tool-{}-args", tool.name), parameters(tool))
.map_err(|e| schema_refused(tool.name, &e))?;
let body = format!(
r#""{{" space "\"name\"" space ":" space "\"{name}\"" space "," space "\"arguments\"" space ":" space {args} space "}}""#,
name = tool.name,
);
alternatives.push(builder.add_rule(&format!("tool-{}-call", tool.name), &body));
}
let call = builder.add_rule("tool-call", &alternatives.join(" | "));
let payload = if array {
builder.add_rule("tool-call-list", &format!(r#""[" space {call} space "]""#))
} else {
call
};
Ok((
block(
markers.open,
&format!("space {payload} space"),
markers.close,
),
trigger(markers.open)?,
))
}