fix(agent-tools): render tool schemas without HTML-safe escapes

minijinja's tojson escapes `<`, `>`, `&` and `'` so its output can ride
a script tag. A system prompt is not a script tag, so every apostrophe
in a field description reached the model as \u0027 and every
`<provider>` as \u003c: harder to read, and more tokens for the same
text.

The renderer registers a json filter that serializes through serde_json
and leaves those characters alone, and both tool sections use it.
tojson stays for a user template whose output does feed a page. The
serialization is also compact, which drops the spaces tojson puts
after every separator.

The tool row is `name | arguments` with the description indented
underneath, so a multi-line description can no longer push the schema
column below another tool's prose, where it read as part of that tool.
This commit is contained in:
vhaudiquet committed 2026-11-11 18:13:17 +01:00
1 parent cfb3178346
commit 1663dee661
3 files changed
+8 -6

No files matched your search

+4 -3
View File
@@ -19,11 +19,12 @@ it, is the reply the user sees.
{% if tools %}
## Tools
You act through these tools (name | description | arguments). The
arguments are the JSON schema a call must satisfy.
You act through these tools: each entry is its name and arguments, then
what it is for. The arguments are the JSON schema a call must satisfy.
{% for tool in tools %}
- {{ tool.name }} | {{ tool.description }} | {{ tool.parameters | tojson }}
- {{ tool.name }} | {{ tool.parameters | json }}
{{ tool.description | indent(2, true) }}
{% endfor %}
{% endif %}
+3 -2
View File
@@ -518,9 +518,10 @@ mod tests {
None,
)
.expect("renders");
assert!(prompt.contains("- my_tool | its purpose | "), "{prompt}");
assert!(prompt.contains("- my_tool | {"), "{prompt}");
assert!(prompt.contains("\n its purpose"), "{prompt}");
assert!(
prompt.contains(r#""required": ["kind"]"#),
prompt.contains(r#""required":["kind"]"#),
"the JSON schema rendered: {prompt}"
);
}
+1 -1
View File
@@ -1218,7 +1218,7 @@ mod tests {
);
}
assert!(
prompt.contains(r#""required": ["command"]"#),
prompt.contains(r#""required":["command"]"#),
"bash's schema renders: {prompt}"
);
assert!(prompt.contains("final message"), "{prompt}");