use askama::Template;
use super::backend::AgentBackendError;
use super::protocol;
const REPAIR_RESPONSE_PREVIEW_MAX_CHARS: usize = 500;
#[derive(Template)]
#[template(path = "protocol_repair_prompt.md", escape = "none")]
struct ProtocolRepairPromptTemplate<'a> {
parse_error: &'a str,
response_json_schema: &'a str,
response_preview: &'a str,
}
pub(crate) fn build_protocol_repair_prompt(
parse_error: &str,
malformed_response: &str,
) -> Result<String, AgentBackendError> {
let response_json_schema = protocol::agent_response_json_schema_json();
let response_preview = truncate_preview(malformed_response, REPAIR_RESPONSE_PREVIEW_MAX_CHARS);
let template = ProtocolRepairPromptTemplate {
parse_error,
response_json_schema: &response_json_schema,
response_preview: &response_preview,
};
template.render().map_err(|error| {
AgentBackendError::CommandBuild(format!(
"Failed to render `protocol_repair_prompt.md`: {error}"
))
})
}
fn truncate_preview(raw: &str, max_chars: usize) -> String {
let preview: String = raw.chars().take(max_chars).collect();
let total_chars = raw.chars().count();
if total_chars <= max_chars {
return preview;
}
format!("{preview}\n... [{} more chars]", total_chars - max_chars)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_build_protocol_repair_prompt_includes_error_and_preview() {
let parse_error = "response is not valid protocol JSON: invalid JSON";
let malformed_response = "plain text response";
let repair_prompt = build_protocol_repair_prompt(parse_error, malformed_response)
.expect("repair prompt should render");
assert!(repair_prompt.contains(parse_error));
assert!(repair_prompt.contains("plain text response"));
assert!(repair_prompt.contains("Structured response protocol:"));
assert!(repair_prompt.contains("Authoritative JSON Schema:"));
assert!(repair_prompt.contains("\"answer\""));
}
#[test]
fn test_build_protocol_repair_prompt_truncates_long_response() {
let parse_error = "schema validation failed";
let malformed_response = "x".repeat(1000);
let repair_prompt = build_protocol_repair_prompt(parse_error, &malformed_response)
.expect("repair prompt should render");
assert!(repair_prompt.contains("500 more chars"));
assert!(!repair_prompt.contains(&malformed_response));
}
#[test]
fn test_build_protocol_repair_prompt_contains_protocol_marker() {
let repair_prompt =
build_protocol_repair_prompt("error", "response").expect("repair prompt should render");
assert!(repair_prompt.contains("Structured response protocol:"));
}
#[test]
fn test_truncate_preview_keeps_short_responses_intact() {
let preview = truncate_preview("short", 500);
assert_eq!(preview, "short");
}
#[test]
fn test_truncate_preview_truncates_long_responses() {
let long_response = "a".repeat(600);
let preview = truncate_preview(&long_response, 500);
assert!(preview.starts_with(&"a".repeat(500)));
assert!(preview.contains("100 more chars"));
}
}