Skip to main content

ironflow_engine/executor/
decision.rs

1//! Decision step executor.
2//!
3//! Runs a [`DecisionConfig`](crate::config::DecisionConfig) against a
4//! [`DecisionProvider`](ironflow_core::decision::DecisionProvider) and records the
5//! USD cost reported by [`DecisionUsage::cost_usd`](ironflow_core::decision::DecisionUsage::cost_usd).
6
7use std::sync::Arc;
8use std::time::Instant;
9
10use ironflow_core::decision::{DecisionOutput, DecisionProvider};
11use ironflow_core::error::OperationError;
12use rust_decimal::Decimal;
13
14use crate::config::DecisionConfig;
15use crate::error::EngineError;
16
17/// The result of executing a decision step: the typed output plus accounting.
18#[derive(Debug, Clone)]
19pub struct DecisionExecution {
20    /// The typed answers returned by the provider.
21    pub output: DecisionOutput,
22    /// Wall-clock duration in milliseconds.
23    pub duration_ms: u64,
24    /// Cost in USD, imputed from input tokens at the Jev rate.
25    pub cost_usd: Decimal,
26    /// Input token count.
27    pub input_tokens: u64,
28    /// Output token count (typically zero for System One models).
29    pub output_tokens: u64,
30}
31
32/// Execute a decision request against the provider.
33///
34/// # Errors
35///
36/// Returns [`EngineError::Operation`] wrapping the provider error if the backend
37/// fails, times out, or returns an unparseable response.
38pub async fn execute_decision(
39    provider: &Arc<dyn DecisionProvider>,
40    config: &DecisionConfig,
41) -> Result<DecisionExecution, EngineError> {
42    let request = config.to_request();
43    let start = Instant::now();
44    let output = provider
45        .decide(&request)
46        .await
47        .map_err(OperationError::from)?;
48    let duration_ms = start.elapsed().as_millis() as u64;
49    let input_tokens = output.usage.input_tokens;
50    let output_tokens = output.usage.output_tokens;
51    Ok(DecisionExecution {
52        cost_usd: output.usage.cost_usd(),
53        output,
54        duration_ms,
55        input_tokens,
56        output_tokens,
57    })
58}