roder-tools 0.1.0

Agentic software development tools and SDKs for Roder.
Documentation
use serde::Deserialize;
use serde_json::{Value, json};

const CONCISE_LINE_CHARS: usize = 240;

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "snake_case")]
pub(crate) enum ResponseFormat {
    #[default]
    Concise,
    Detailed,
}

impl ResponseFormat {
    pub(crate) fn schema_property() -> Value {
        json!({
            "type": "string",
            "enum": ["concise", "detailed"],
            "default": "concise",
            "description": "concise keeps model-facing output bounded; detailed preserves full returned line text."
        })
    }

    pub(crate) fn as_str(self) -> &'static str {
        match self {
            Self::Concise => "concise",
            Self::Detailed => "detailed",
        }
    }

    pub(crate) fn format_line(self, line: &str) -> String {
        match self {
            Self::Concise => truncate_line(line, CONCISE_LINE_CHARS),
            Self::Detailed => line.to_string(),
        }
    }
}

fn truncate_line(line: &str, max_chars: usize) -> String {
    if line.chars().count() <= max_chars {
        return line.to_string();
    }
    let suffix = "...";
    let keep = max_chars.saturating_sub(suffix.len());
    let mut output = line.chars().take(keep).collect::<String>();
    output.push_str(suffix);
    output
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn response_format_concise_truncates_long_lines() {
        let line = format!("prefix {}", "x".repeat(400));

        let concise = ResponseFormat::Concise.format_line(&line);
        let detailed = ResponseFormat::Detailed.format_line(&line);

        assert!(concise.chars().count() <= CONCISE_LINE_CHARS);
        assert!(concise.ends_with("..."));
        assert_eq!(detailed, line);
    }
}