use leviath_core::output::{FinalOutput, OutputSpec};
use crate::components::ContextWindow;
pub const FINAL_OUTPUT_REGION: &str = "final_output";
pub const FINAL_OUTPUT_REGION_TOKENS: usize = 2_000;
const MIRROR_TRUNCATION_MARKER: &str =
"\n[...the full answer is on the run's final output, not in context]";
pub fn is_output_tool(name: &str) -> bool {
name == leviath_core::blueprint::SUBMIT_OUTPUT_TOOL
}
pub fn handle_output_tool(
args: &serde_json::Value,
spec: Option<&OutputSpec>,
validators: Option<&crate::components::OutputValidators>,
stage: &str,
now: i64,
workdir: Option<&std::path::Path>,
window: &mut ContextWindow,
) -> (String, Option<FinalOutput>) {
let Some(content) = args.get("content").and_then(|v| v.as_str()) else {
return ("[error] missing 'content' argument".to_string(), None);
};
if content.trim().is_empty() {
return (
"[error] final output is empty - submit the answer itself, not a placeholder"
.to_string(),
None,
);
}
if let Some(format) = spec.and_then(|s| s.format.as_deref())
&& let Err(reason) = leviath_tools::validate::format::check(Some(format), content)
{
return (
format!("[error] the final output is not valid {format}: {reason}"),
None,
);
}
if let Some(schema) = spec.and_then(|s| s.schema.as_ref()) {
match leviath_tools::validate_output(schema, content) {
leviath_tools::ArgValidation::Invalid(message) => return (message, None),
leviath_tools::ArgValidation::SchemaUnusable(e) => {
tracing::warn!(
stage = %stage,
error = %e,
"output schema did not compile; recording the submission unchecked"
);
}
leviath_tools::ArgValidation::Valid => {}
}
}
if let Some(script) = spec.and_then(|s| s.validator.as_deref())
&& let Some(validator) = validators.and_then(|v| v.0.get(script))
{
match leviath_scripting::output_validator::validate(validator, content) {
leviath_scripting::output_validator::Verdict::Invalid(reason) => {
return (
format!("[error] the final output was rejected: {reason}"),
None,
);
}
leviath_scripting::output_validator::Verdict::Unusable(reason) => {
tracing::warn!(
stage = %stage,
error = %reason,
"output validator failed to run; recording the submission unchecked"
);
}
leviath_scripting::output_validator::Verdict::Valid => {}
}
}
let artifacts = match resolve_artifacts(args, workdir) {
Ok(paths) => paths,
Err(message) => return (message, None),
};
let output = FinalOutput::new(
content,
spec.and_then(|s| s.format.clone()),
stage.to_string(),
now,
)
.with_artifacts(artifacts);
mirror_into_region(window, &output.content);
let mut ack = "Recorded as this run's final output.".to_string();
if output.truncated {
ack.push_str(&format!(
" It exceeded the {} KiB limit and was truncated; submit a shorter answer, or write \
the long form to a file and summarise it here.",
leviath_core::output::MAX_FINAL_OUTPUT_BYTES / 1024
));
}
(ack, Some(output))
}
fn resolve_artifacts(
args: &serde_json::Value,
workdir: Option<&std::path::Path>,
) -> Result<Vec<String>, String> {
let listed: Vec<&str> = args
.get("artifacts")
.and_then(|v| v.as_array())
.map(|a| {
a.iter()
.filter_map(|v| v.as_str())
.filter(|p| !p.trim().is_empty())
.collect()
})
.unwrap_or_default();
if listed.is_empty() {
return Ok(Vec::new());
}
let Some(workdir) = workdir else {
return Err(
"[error] cannot record artifacts: this run has no working directory to resolve \
them against"
.to_string(),
);
};
let (kept, rejected): (Vec<&str>, Vec<&str>) = listed
.into_iter()
.partition(|p| leviath_core::resolves_within(&workdir.join(p), workdir));
match rejected.is_empty() {
true => Ok(kept.into_iter().map(str::to_string).collect()),
false => Err(format!(
"[error] these artifact paths do not resolve inside the working directory: {}",
rejected.join(", ")
)),
}
}
fn mirror_into_region(window: &mut ContextWindow, content: &str) {
let budget = {
let Some(region) = window.get_region_mut(FINAL_OUTPUT_REGION) else {
return;
};
region.clear();
region.max_tokens
};
let mirrored = fit_to_region(content, budget);
let tokens = leviath_core::estimate_tokens(&mirrored);
window.current_tokens = window.calculate_tokens();
let _ = window.add_to_region(FINAL_OUTPUT_REGION, mirrored, tokens);
}
fn fit_to_region(content: &str, budget: usize) -> String {
let allowed = budget.saturating_mul(4);
if content.len() <= allowed {
return content.to_string();
}
let Some(room) = allowed.checked_sub(MIRROR_TRUNCATION_MARKER.len()) else {
return String::new();
};
format!(
"{}{MIRROR_TRUNCATION_MARKER}",
leviath_core::truncate_at_boundary(content, room)
)
}
#[cfg(test)]
mod tests;