Skip to main content

ironflow_core/
lib.rs

1//! # ironflow-core
2//!
3//! Core building blocks for the **ironflow** workflow engine. This crate
4//! provides composable, async operations that can be chained via plain Rust
5//! variables to build headless CI/CD, DevOps, and AI-powered workflows.
6//!
7//! # Operations
8//!
9//! | Operation | Description |
10//! |-----------|-------------|
11//! | [`Shell`](operations::shell::Shell) | Execute a shell command with timeout, env control, and `kill_on_drop`. |
12//! | [`Agent`](operations::agent::Agent) | Invoke an AI agent (Claude Code by default) with structured output support. |
13//! | [`Http`](operations::http::Http) | Perform HTTP requests via [`reqwest`] with builder-pattern ergonomics. |
14//!
15//! # Provider trait
16//!
17//! The [`AgentProvider`](provider::AgentProvider) trait abstracts the AI
18//! backend. The built-in [`ClaudeCodeProvider`](providers::claude::ClaudeCodeProvider)
19//! shells out to the `claude` CLI; swap it for
20//! [`RecordReplayProvider`](providers::record_replay::RecordReplayProvider)
21//! in tests for deterministic, zero-cost replay.
22//!
23//! # Known limitations: Structured output
24//!
25//! When using [`AgentConfig::output::<T>()`](provider::AgentConfig::output) to request
26//! structured (typed) output from the Claude CLI, be aware of these upstream bugs:
27//!
28//! | Issue | Impact |
29//! |-------|--------|
30//! | [claude-code#18536] | `structured_output` is always `null` when tools are used alongside `--json-schema`. ironflow prevents this at compile time via typestate (tools and schema are mutually exclusive). |
31//! | [claude-code#9058] | The CLI does not validate output against the provided JSON schema -- non-conforming JSON may be returned. |
32//! | [claude-agent-sdk-python#502] | Wrapper objects with a single array field may be flattened to a bare array (e.g. `[...]` instead of `{"items": [...]}`). |
33//! | [claude-agent-sdk-python#374] | The wrapping behavior is non-deterministic: the same prompt can produce differently shaped output across runs. |
34//!
35//! **Recommended workarounds:**
36//!
37//! 1. **Two-step pattern**: use one agent with tools to gather data, then a second
38//!    agent with `.output::<T>()` (no tools) to structure the result.
39//! 2. **Defensive deserialization**: when deserializing structured output, handle
40//!    both the expected wrapper object and a bare array/value as fallback.
41//! 3. **`max_turns >= 2`**: structured output requires at least 2 turns; setting
42//!    `max_turns(1)` with a schema will fail with `error_max_turns`.
43//!
44//! [claude-code#18536]: https://github.com/anthropics/claude-code/issues/18536
45//! [claude-code#9058]: https://github.com/anthropics/claude-code/issues/9058
46//! [claude-agent-sdk-python#502]: https://github.com/anthropics/claude-agent-sdk-python/issues/502
47//! [claude-agent-sdk-python#374]: https://github.com/anthropics/claude-agent-sdk-python/issues/374
48//!
49//! # Quick start
50//!
51//! ```no_run
52//! use ironflow_core::prelude::*;
53//!
54//! # async fn example() -> Result<(), OperationError> {
55//! let files = Shell::new("ls -la").await?;
56//!
57//! let provider = ClaudeCodeProvider::new();
58//! let review = Agent::new()
59//!     .prompt(&format!("Summarise:\n{}", files.stdout()))
60//!     .model(Model::HAIKU)
61//!     .max_budget_usd(0.10)
62//!     .run(&provider)
63//!     .await?;
64//!
65//! println!("{}", review.text());
66//! # Ok(())
67//! # }
68//! ```
69
70pub mod dry_run;
71pub mod error;
72pub mod metric_names;
73pub mod operation;
74pub mod parallel;
75pub mod pricing;
76pub mod provider;
77pub mod providers;
78pub mod retry;
79pub mod schema_transform;
80#[cfg(feature = "opentelemetry")]
81pub mod telemetry;
82#[cfg(test)]
83pub(crate) mod test_support;
84pub mod trace_context;
85pub mod tracker;
86pub mod utils;
87
88/// Workflow operations (shell commands, agent calls, HTTP requests).
89pub mod operations {
90    pub mod agent;
91    pub mod http;
92    pub mod shell;
93}
94
95/// Re-exports of the most commonly used types.
96pub mod prelude {
97    pub use crate::dry_run::{DryRunGuard, is_dry_run, set_dry_run};
98    pub use crate::error::{AgentError, OperationError};
99    pub use crate::operation::{
100        NoopSecretResolver, Operation, OperationContext, SecretResolver, SecretValue,
101        TypedOperation,
102    };
103    pub use crate::operations::agent::{Agent, AgentResult, Model, PermissionMode};
104    pub use crate::operations::http::{Http, HttpOutput};
105    pub use crate::operations::shell::{Shell, ShellOutput};
106    pub use crate::parallel::{try_join_all, try_join_all_limited};
107    pub use crate::pricing::{CostBreakdown, PricingSource, StaticPricing};
108    pub use crate::provider::{AgentConfig, AgentProvider, DebugMessage, DebugToolCall, LogSink};
109    pub use crate::providers::claude::ClaudeCodeProvider;
110    pub use crate::providers::record_replay::RecordReplayProvider;
111    pub use crate::retry::RetryPolicy;
112    pub use crate::trace_context::WorkflowTraceContext;
113    pub use crate::tracker::WorkflowTracker;
114    pub use schemars::JsonSchema;
115    pub use serde::{Deserialize, Serialize};
116
117    #[cfg(feature = "provider-anthropic-api")]
118    pub use crate::providers::http::AnthropicApiProvider;
119    #[cfg(feature = "provider-gemini")]
120    pub use crate::providers::http::GeminiProvider;
121    #[cfg(feature = "provider-mistral")]
122    pub use crate::providers::http::MistralProvider;
123    #[cfg(feature = "provider-nvidia")]
124    pub use crate::providers::http::NvidiaProvider;
125    #[cfg(feature = "provider-openai")]
126    pub use crate::providers::http::OpenAiProvider;
127
128    pub use crate::providers::router::{ProviderMatcher, ProviderRouter};
129}