const HARMONY_CHANNEL_MARKERS: &[&str] =
&["<|channel|>", "<|message|>", "<|recipient|>", "<|end|>"];
const TOOL_NAMESPACE_PREFIXES: &[&str] = &["tool.", "tools.", "functions.", "function."];
pub(crate) fn normalize_tool_name(name: &str) -> String {
let trimmed = name.trim();
for marker in HARMONY_CHANNEL_MARKERS {
if let Some(pos) = trimmed.find(marker) {
let head = trimmed[..pos].trim();
if !head.is_empty() {
return head.to_string();
}
}
}
trimmed.to_string()
}
fn strip_tool_namespace_prefix(name: &str) -> Option<String> {
let trimmed = name.trim();
for prefix in TOOL_NAMESPACE_PREFIXES {
if let Some(rest) = trimmed.strip_prefix(prefix) {
let bare = rest.trim();
if !bare.is_empty() {
return Some(bare.to_string());
}
}
}
None
}
fn recover_namespaced_name(name: String) -> String {
if is_generic_wrapper_name(&name) {
return name;
}
strip_tool_namespace_prefix(&name).unwrap_or(name)
}
pub(crate) fn normalize_tool_call_shape(
name: &str,
arguments: serde_json::Value,
) -> (String, serde_json::Value) {
let normalized_name = normalize_tool_name(name);
let is_marker_wrapper = is_harmony_marker_wrapper_name(&normalized_name);
if !is_generic_wrapper_name(&normalized_name) && !is_marker_wrapper {
return (recover_namespaced_name(normalized_name), arguments);
}
if let Some((inner_name, inner_arguments)) = unwrap_generic_tool_arguments(&arguments) {
return (
recover_namespaced_name(normalize_tool_name(&inner_name)),
inner_arguments,
);
}
if is_marker_wrapper {
if let Some(inferred_name) = infer_tool_name_from_arguments(&arguments) {
return (inferred_name, arguments);
}
}
(normalized_name, arguments)
}
fn is_generic_wrapper_name(name: &str) -> bool {
matches!(
name,
"tool" | "tool.call" | "tool.exec" | "function" | "function.call" | "call"
)
}
fn is_harmony_marker_wrapper_name(name: &str) -> bool {
let trimmed = name.trim();
trimmed.starts_with("<|") && trimmed.contains("|>")
}
fn infer_tool_name_from_arguments(arguments: &serde_json::Value) -> Option<String> {
let object = arguments.as_object()?;
let has_command_shape = object
.get("command")
.or_else(|| object.get("commands"))
.or_else(|| object.get("cmd"))
.is_some();
if has_command_shape {
return Some("run".to_string());
}
None
}
fn unwrap_generic_tool_arguments(
arguments: &serde_json::Value,
) -> Option<(String, serde_json::Value)> {
let object = arguments.as_object()?;
let inner_name = object
.get("name")
.or_else(|| object.get("tool"))
.and_then(|value| value.as_str())
.map(str::trim)
.filter(|value| !value.is_empty())?
.to_string();
let inner_arguments = match object
.get("args")
.or_else(|| object.get("arguments"))
.or_else(|| object.get("parameters"))
{
Some(value @ serde_json::Value::Object(_)) => value.clone(),
Some(serde_json::Value::String(raw)) => match serde_json::from_str(raw) {
Ok(value @ serde_json::Value::Object(_)) => value,
_ => return None,
},
Some(serde_json::Value::Null) | None => serde_json::Value::Object(Default::default()),
Some(_) => return None,
};
Some((inner_name, inner_arguments))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn strips_harmony_channel_suffix_from_tool_name() {
assert_eq!(normalize_tool_name("run<|channel|>commentary"), "run");
}
#[test]
fn unwraps_generic_tool_envelope() {
let (name, arguments) = normalize_tool_call_shape(
"tool.call",
serde_json::json!({
"name": "look",
"args": {"intent": "read", "file": "src/lib.rs"}
}),
);
assert_eq!(name, "look");
assert_eq!(arguments["intent"], "read");
assert_eq!(arguments["file"], "src/lib.rs");
}
#[test]
fn infers_run_from_harmony_marker_wrapper_command_shape() {
let arguments = serde_json::json!({"command": "cargo test"});
let (name, normalized_arguments) =
normalize_tool_call_shape("<|constrain|>json", arguments.clone());
assert_eq!(name, "run");
assert_eq!(normalized_arguments, arguments);
}
#[test]
fn preserves_unrecognized_harmony_marker_wrapper_shape() {
let arguments = serde_json::json!({"intent": "read", "file": "src/lib.rs"});
let (name, normalized_arguments) =
normalize_tool_call_shape("<|constrain|>json", arguments.clone());
assert_eq!(name, "<|constrain|>json");
assert_eq!(normalized_arguments, arguments);
}
#[test]
fn preserves_non_wrapper_tool_call() {
let arguments = serde_json::json!({"command": "cargo test"});
let (name, normalized_arguments) = normalize_tool_call_shape("run", arguments.clone());
assert_eq!(name, "run");
assert_eq!(normalized_arguments, arguments);
}
#[test]
fn strips_tool_namespace_prefix_from_native_name() {
let arguments = serde_json::json!({"intent": "read", "file": "src/lib.rs"});
let (name, normalized_arguments) =
normalize_tool_call_shape("tool.look", arguments.clone());
assert_eq!(name, "look");
assert_eq!(normalized_arguments, arguments);
}
#[test]
fn strips_functions_namespace_prefix_from_native_name() {
let arguments = serde_json::json!({"query": "needle"});
let (name, normalized_arguments) =
normalize_tool_call_shape("functions.search", arguments.clone());
assert_eq!(name, "search");
assert_eq!(normalized_arguments, arguments);
}
#[test]
fn strips_tools_and_function_namespace_prefixes() {
let (name, _) = normalize_tool_call_shape("tools.run", serde_json::json!({}));
assert_eq!(name, "run");
let (name, _) = normalize_tool_call_shape("function.edit", serde_json::json!({}));
assert_eq!(name, "edit");
}
#[test]
fn generic_tool_exec_wrapper_unwraps_inner_name_not_prefix_stripped() {
let (name, arguments) = normalize_tool_call_shape(
"tool.exec",
serde_json::json!({
"name": "run",
"args": {"command": "cargo test"}
}),
);
assert_eq!(name, "run");
assert_eq!(arguments["command"], "cargo test");
}
#[test]
fn generic_function_call_wrapper_is_not_prefix_stripped() {
let (name, arguments) = normalize_tool_call_shape(
"function.call",
serde_json::json!({
"name": "search",
"args": {"query": "needle"}
}),
);
assert_eq!(name, "search");
assert_eq!(arguments["query"], "needle");
}
#[test]
fn strips_namespace_prefix_after_harmony_suffix() {
let (name, _) =
normalize_tool_call_shape("tool.run<|channel|>commentary", serde_json::json!({}));
assert_eq!(name, "run");
}
#[test]
fn leaves_already_bare_name_unchanged() {
let arguments = serde_json::json!({"intent": "read"});
let (name, normalized_arguments) = normalize_tool_call_shape("look", arguments.clone());
assert_eq!(name, "look");
assert_eq!(normalized_arguments, arguments);
}
}