#[derive(Clone, Copy, PartialEq, Eq)]
pub enum DumpMode {
Off,
Summary,
Full,
}
pub fn dump_mode() -> DumpMode {
match std::env::var("DIRGE_DUMP_REQUESTS").ok().as_deref() {
Some("full") | Some("2") => DumpMode::Full,
Some(s) if !s.is_empty() && s != "0" && !s.eq_ignore_ascii_case("off") => DumpMode::Summary,
_ => DumpMode::Off,
}
}
pub fn enabled() -> bool {
dump_mode() != DumpMode::Off
}
pub fn dump_oneshot(purpose: &str, provider: &str, model: &str, preamble: &str, prompt: &str) {
match dump_mode() {
DumpMode::Off => {}
DumpMode::Summary => tracing::info!(
target: "dirge::wire",
purpose,
kind = "one-shot",
provider,
model,
tools = 0,
preamble_bytes = preamble.len(),
prompt_bytes = prompt.len(),
"provider request",
),
DumpMode::Full => tracing::info!(
target: "dirge::wire",
purpose,
kind = "one-shot",
provider,
model,
tools = 0,
preamble_bytes = preamble.len(),
prompt_bytes = prompt.len(),
preamble = %preamble,
prompt = %prompt,
"provider request",
),
}
}
pub fn dump_turn(
provider: Option<&str>,
model: &str,
system_prompt: &str,
history_len: usize,
messages_bytes: usize,
tool_names: &[String],
reasoning: bool,
) {
match dump_mode() {
DumpMode::Off => {}
DumpMode::Summary => tracing::info!(
target: "dirge::wire",
purpose = "turn",
kind = "agent",
provider = provider.unwrap_or("default"),
model,
system_bytes = system_prompt.len(),
history_len,
messages_bytes,
tool_count = tool_names.len(),
tools = ?tool_names,
reasoning,
"provider request",
),
DumpMode::Full => tracing::info!(
target: "dirge::wire",
purpose = "turn",
kind = "agent",
provider = provider.unwrap_or("default"),
model,
system_bytes = system_prompt.len(),
history_len,
messages_bytes,
tool_count = tool_names.len(),
tools = ?tool_names,
reasoning,
system = %system_prompt,
"provider request",
),
}
}
#[allow(dead_code)]
pub fn format_oneshot_summary(
purpose: &str,
provider: &str,
model: &str,
preamble: &str,
prompt: &str,
) -> String {
format!(
"oneshot purpose=\"{purpose}\" provider={provider} model={model} tools=0 preamble_bytes={} prompt_bytes={}",
preamble.len(),
prompt.len(),
)
}
#[allow(dead_code)]
pub fn format_turn_summary(
provider: &str,
model: &str,
system_prompt: &str,
history_len: usize,
messages_bytes: usize,
tool_names: &[String],
reasoning: bool,
) -> String {
format!(
"turn provider={provider} model={model} system_bytes={} history_len={history_len} messages_bytes={messages_bytes} tool_count={} tools={tool_names:?} reasoning={reasoning}",
system_prompt.len(),
tool_names.len(),
)
}
#[cfg(test)]
mod tests {
use super::*;
fn with_env<T>(val: Option<&str>, f: impl FnOnce() -> T) -> T {
match val {
Some(v) => unsafe { std::env::set_var("DIRGE_DUMP_REQUESTS", v) },
None => unsafe { std::env::remove_var("DIRGE_DUMP_REQUESTS") },
}
let out = f();
unsafe { std::env::remove_var("DIRGE_DUMP_REQUESTS") };
out
}
#[test]
fn dump_mode_parses_env() {
with_env(None, || assert!(matches!(dump_mode(), DumpMode::Off)));
with_env(Some(""), || assert!(matches!(dump_mode(), DumpMode::Off)));
with_env(Some("0"), || assert!(matches!(dump_mode(), DumpMode::Off)));
with_env(Some("off"), || {
assert!(matches!(dump_mode(), DumpMode::Off))
});
with_env(Some("1"), || {
assert!(matches!(dump_mode(), DumpMode::Summary))
});
with_env(Some("yes"), || {
assert!(matches!(dump_mode(), DumpMode::Summary))
});
with_env(Some("full"), || {
assert!(matches!(dump_mode(), DumpMode::Full))
});
with_env(Some("2"), || assert!(matches!(dump_mode(), DumpMode::Full)));
}
#[test]
fn format_oneshot_summary_produces_expected() {
let out = format_oneshot_summary(
"critic",
"anthropic",
"claude-sonnet-4-20250514",
"You are a code critic.",
"Review this diff:\nfn main() {}",
);
assert!(out.starts_with("oneshot "));
assert!(out.contains(r#"purpose="critic""#));
assert!(out.contains("provider=anthropic"));
assert!(out.contains("model=claude-sonnet-4-20250514"));
assert!(out.contains("tools=0"));
assert!(out.contains("preamble_bytes=22"));
assert!(out.contains("prompt_bytes=30"));
}
#[test]
fn format_turn_summary_produces_expected() {
let tools = vec!["bash".to_string(), "read".to_string()];
let out = format_turn_summary(
"openrouter",
"o4-mini",
"You are an AI coding agent.",
12,
8432,
&tools,
true,
);
assert!(out.starts_with("turn "));
assert!(out.contains("provider=openrouter"));
assert!(out.contains("model=o4-mini"));
assert!(out.contains("system_bytes=27"));
assert!(out.contains("history_len=12"));
assert!(out.contains("messages_bytes=8432"));
assert!(out.contains("tool_count=2"));
assert!(out.contains("reasoning=true"));
assert!(out.contains("bash"));
assert!(out.contains("read"));
}
}