pub struct StepOutput {
pub output: Value,
pub duration_ms: u64,
pub cost_usd: Decimal,
pub input_tokens: Option<u64>,
pub cache_read_input_tokens: Option<u64>,
pub cache_creation_input_tokens: Option<u64>,
pub output_tokens: Option<u64>,
pub model: Option<String>,
pub debug_messages: Option<Vec<DebugMessage>>,
pub artifacts: StepArtifacts,
}Expand description
Result of executing a single step.
Fields§
§output: ValueSerialized output (stdout for shell, body for http, value for agent).
For agent steps with a JSON schema, the value may not strictly conform
to the schema: Claude CLI can flatten wrapper objects with a single
array field, returning a bare array instead of {"items": [...]}.
Callers should handle both the expected wrapper and a bare value.
duration_ms: u64Wall-clock duration in milliseconds.
cost_usd: DecimalCost in USD (agent steps only).
input_tokens: Option<u64>Uncached input token count (agent steps only).
cache_read_input_tokens: Option<u64>Input tokens served from the prompt cache (agent steps only).
cache_creation_input_tokens: Option<u64>Input tokens written to the prompt cache (agent steps only).
output_tokens: Option<u64>Output token count (agent steps only).
model: Option<String>Model identifier used for agent steps (e.g. "claude-sonnet-4-20250514").
debug_messages: Option<Vec<DebugMessage>>Conversation trace from verbose agent invocations.
artifacts: StepArtifactsArtifacts the step can hand out through artifact.
Filled by the workflow context; executors leave the default.
Implementations§
Source§impl StepOutput
impl StepOutput
Sourcepub fn artifact(&self, name: &str) -> Result<ArtifactRef, EngineError>
pub fn artifact(&self, name: &str) -> Result<ArtifactRef, EngineError>
Handle on an artifact this step declared, to feed a later step.
name is the file name the artifact is stored under, without its
directory. It must match the file-name part of one of the step’s
output patterns, so a typo fails
here rather than in the step that consumes it.
§Errors
Returns EngineError::ArtifactNotDeclared when no declared output
covers name.
§Examples
use ironflow_engine::config::ShellConfig;
use ironflow_engine::context::WorkflowContext;
use ironflow_engine::error::EngineError;
let build = ctx
.shell("build", ShellConfig::new("cargo build").output("target/*.log"))
.await?;
let log = build.artifact("build.log")?;
assert!(build.artifact("build.txt").is_err());
ctx.shell("archive", ShellConfig::new("gzip build.log").input(&log)).await?;Source§impl StepOutput
impl StepOutput
Sourcepub fn total_tokens(&self) -> u64
pub fn total_tokens(&self) -> u64
Total tokens consumed by the step: uncached input, cache reads, cache writes and output. Missing counts are treated as 0 and the sum saturates.
§Examples
use ironflow_engine::executor::{StepArtifacts, StepOutput};
use rust_decimal::Decimal;
use serde_json::json;
let output = StepOutput {
output: json!("ok"),
duration_ms: 10,
cost_usd: Decimal::ZERO,
input_tokens: Some(100),
cache_read_input_tokens: Some(5000),
cache_creation_input_tokens: Some(200),
output_tokens: Some(50),
model: None,
debug_messages: None,
artifacts: StepArtifacts::default(),
};
assert_eq!(output.total_tokens(), 5350);Sourcepub fn debug_messages_json(&self) -> Option<Value>
pub fn debug_messages_json(&self) -> Option<Value>
Serialize debug messages to a JSON Value for store persistence.
Returns None when verbose mode was off (no messages captured).
Sourcepub fn exit_code(&self) -> Option<i64>
pub fn exit_code(&self) -> Option<i64>
Exit code of a shell step.
Returns None for non-shell steps or when the field is absent.
§Examples
use ironflow_engine::executor::{StepArtifacts, StepOutput};
use rust_decimal::Decimal;
use serde_json::json;
let output = StepOutput {
output: json!({"stdout": "ok\n", "stderr": "", "exit_code": 0}),
duration_ms: 3,
cost_usd: Decimal::ZERO,
input_tokens: None,
cache_read_input_tokens: None,
cache_creation_input_tokens: None,
output_tokens: None,
model: None,
debug_messages: None,
artifacts: StepArtifacts::default(),
};
assert_eq!(output.exit_code(), Some(0));Sourcepub fn stdout(&self) -> &str
pub fn stdout(&self) -> &str
Standard output of a shell step, or an empty string for other kinds.
§Examples
use ironflow_engine::executor::{StepArtifacts, StepOutput};
use rust_decimal::Decimal;
use serde_json::json;
let output = StepOutput {
output: json!({"stdout": "42 tests passed\n", "stderr": "", "exit_code": 0}),
duration_ms: 3,
cost_usd: Decimal::ZERO,
input_tokens: None,
cache_read_input_tokens: None,
cache_creation_input_tokens: None,
output_tokens: None,
model: None,
debug_messages: None,
artifacts: StepArtifacts::default(),
};
assert!(output.stdout().contains("42 tests"));Sourcepub fn stderr(&self) -> &str
pub fn stderr(&self) -> &str
Standard error of a shell step, or an empty string for other kinds.
§Examples
use ironflow_engine::executor::{StepArtifacts, StepOutput};
use rust_decimal::Decimal;
use serde_json::json;
let output = StepOutput {
output: json!({"stdout": "", "stderr": "warning: unused", "exit_code": 0}),
duration_ms: 3,
cost_usd: Decimal::ZERO,
input_tokens: None,
cache_read_input_tokens: None,
cache_creation_input_tokens: None,
output_tokens: None,
model: None,
debug_messages: None,
artifacts: StepArtifacts::default(),
};
assert_eq!(output.stderr(), "warning: unused");Sourcepub fn status(&self) -> Option<u16>
pub fn status(&self) -> Option<u16>
HTTP status code of an HTTP step.
Returns None for non-HTTP steps or when the field is absent.
§Examples
use ironflow_engine::executor::{StepArtifacts, StepOutput};
use rust_decimal::Decimal;
use serde_json::json;
let output = StepOutput {
output: json!({"status": 204, "body": ""}),
duration_ms: 3,
cost_usd: Decimal::ZERO,
input_tokens: None,
cache_read_input_tokens: None,
cache_creation_input_tokens: None,
output_tokens: None,
model: None,
debug_messages: None,
artifacts: StepArtifacts::default(),
};
assert_eq!(output.status(), Some(204));Sourcepub fn body(&self) -> &str
pub fn body(&self) -> &str
Response body of an HTTP step, or an empty string for other kinds.
§Examples
use ironflow_engine::executor::{StepArtifacts, StepOutput};
use rust_decimal::Decimal;
use serde_json::json;
let output = StepOutput {
output: json!({"status": 200, "body": "{\"ok\":true}"}),
duration_ms: 3,
cost_usd: Decimal::ZERO,
input_tokens: None,
cache_read_input_tokens: None,
cache_creation_input_tokens: None,
output_tokens: None,
model: None,
debug_messages: None,
artifacts: StepArtifacts::default(),
};
assert_eq!(output.body(), "{\"ok\":true}");Sourcepub fn text(&self) -> &str
pub fn text(&self) -> &str
Text answer of an agent step without structured output, or an empty string for other kinds.
§Examples
use ironflow_engine::executor::{StepArtifacts, StepOutput};
use rust_decimal::Decimal;
use serde_json::json;
let answer = StepOutput {
output: json!("Looks good."),
duration_ms: 3,
cost_usd: Decimal::ZERO,
input_tokens: None,
cache_read_input_tokens: None,
cache_creation_input_tokens: None,
output_tokens: None,
model: None,
debug_messages: None,
artifacts: StepArtifacts::default(),
};
assert_eq!(answer.text(), "Looks good.");
assert_eq!(StepOutput { output: json!({"stdout": "x"}), ..answer }.text(), "");Sourcepub fn is_success(&self) -> bool
pub fn is_success(&self) -> bool
Whether the step succeeded from the point of view of its own kind.
- Shell step: the exit code is
0. - HTTP step: the status is in the
2xxrange. - Any other kind:
false, since no success marker is recorded.
Mostly useful after a step configured with allow_failure(), since a
failing step otherwise returns an error from the context method.
§Examples
use ironflow_engine::executor::{StepArtifacts, StepOutput};
use rust_decimal::Decimal;
use serde_json::json;
let shell = StepOutput {
output: json!({"stdout": "", "stderr": "", "exit_code": 1}),
duration_ms: 3,
cost_usd: Decimal::ZERO,
input_tokens: None,
cache_read_input_tokens: None,
cache_creation_input_tokens: None,
output_tokens: None,
model: None,
debug_messages: None,
artifacts: StepArtifacts::default(),
};
assert!(!shell.is_success());
let http = StepOutput { output: json!({"status": 201, "body": ""}), ..shell.clone() };
assert!(http.is_success());Sourcepub fn json<T: DeserializeOwned>(&self) -> Result<T, EngineError>
pub fn json<T: DeserializeOwned>(&self) -> Result<T, EngineError>
Deserialize the step output into T.
Intended for agent steps constrained by a JSON schema, and for custom operations that return structured JSON.
§Errors
Returns EngineError::Serialization when the output does not match T.
§Examples
use ironflow_engine::executor::{StepArtifacts, StepOutput};
use rust_decimal::Decimal;
use serde::Deserialize;
use serde_json::json;
#[derive(Deserialize)]
struct Review {
score: u8,
}
let output = StepOutput {
output: json!({"score": 8}),
duration_ms: 3,
cost_usd: Decimal::ZERO,
input_tokens: None,
cache_read_input_tokens: None,
cache_creation_input_tokens: None,
output_tokens: None,
model: None,
debug_messages: None,
artifacts: StepArtifacts::default(),
};
let review: Review = output.json()?;
assert_eq!(review.score, 8);Trait Implementations§
Source§impl Clone for StepOutput
impl Clone for StepOutput
Source§impl Debug for StepOutput
impl Debug for StepOutput
Source§impl From<&Step> for StepOutput
View a persisted step through the typed StepOutput accessors.
impl From<&Step> for StepOutput
View a persisted step through the typed StepOutput accessors.
Useful to read the steps of a sub-workflow run, listed from the store by
SubWorkflowOutput::run_id. A step
without output (still running, failed, skipped) reads as an empty output.
The model and the debug conversation are not carried over.
§Examples
use ironflow_engine::context::WorkflowContext;
use ironflow_engine::error::EngineError;
use ironflow_engine::executor::{StepOutput, SubWorkflowOutput};
for step in ctx.store().list_steps(child.run_id()).await? {
println!("{}: {}", step.name, StepOutput::from(&step).stdout());
}