use mentra::TerminalOutputSpec;
use serde_json::Value;
use super::RunReport;
#[derive(Debug, Clone, PartialEq)]
pub struct OutputSpec {
pub name: String,
pub description: String,
pub schema: Value,
pub keeps_tools: bool,
}
impl OutputSpec {
pub fn new(name: impl Into<String>, description: impl Into<String>, schema: Value) -> Self {
Self {
name: name.into(),
description: description.into(),
schema,
keeps_tools: false,
}
}
pub fn with_tools(self) -> Self {
Self {
keeps_tools: true,
..self
}
}
pub(crate) fn into_terminal_spec(self) -> TerminalOutputSpec {
let Self {
name,
description,
schema,
keeps_tools,
} = self;
let spec = TerminalOutputSpec::new(name, description, schema);
if keeps_tools { spec.with_tools() } else { spec }
}
}
#[derive(Debug)]
pub struct OutputReport<T, S> {
pub value: T,
pub report: RunReport<S>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn spec() -> OutputSpec {
OutputSpec::new(
"report",
"the verdict you reached on the last turn",
json!({
"type": "object",
"properties": {
"verdict": { "type": "string", "description": "ship or hold" }
},
"required": ["verdict"]
}),
)
}
#[test]
fn a_spec_reaches_mentra_as_the_caller_wrote_it() {
let terminal = spec().into_terminal_spec();
assert_eq!(terminal.tool_name, "report");
assert_eq!(
terminal.description,
"the verdict you reached on the last turn"
);
assert_eq!(
terminal.schema["properties"]["verdict"]["description"],
"ship or hold"
);
}
#[test]
fn a_spec_is_a_value_a_caller_can_keep_and_reuse() {
let template = spec();
assert_eq!(template.clone(), template);
}
#[test]
fn a_shaping_turn_is_what_a_caller_gets_without_asking_for_more() {
assert!(!spec().keeps_tools);
assert!(!spec().into_terminal_spec().keeps_tools);
}
#[test]
fn asking_for_the_toolset_survives_the_trip_to_mentra() {
let terminal = spec().with_tools().into_terminal_spec();
assert!(terminal.keeps_tools);
assert_eq!(terminal.tool_name, "report", "the rest of the spec travels");
}
#[test]
fn asking_for_the_toolset_leaves_the_caller_a_spec_to_reuse() {
let template = spec().with_tools();
assert_eq!(template.clone(), template);
assert_ne!(template, spec(), "and it is not the shaping spec");
}
}