nika_engine/ast/mod.rs
1//! AST Module - Abstract Syntax Tree for YAML workflows
2//!
3//! # Three-Phase Pipeline
4//!
5//! Nika uses a three-phase pipeline similar to rustc:
6//!
7//! ```text
8//! YAML → raw::parse → analyzer::analyze → lower → Workflow → Runtime
9//! ```
10//!
11//! 1. **raw** — Parsed from YAML with full span tracking (line:col)
12//! 2. **analyzed** — Validated, references resolved, TaskId interning
13//! 3. **runtime types** — `Workflow`, `Task`, etc. consumed by the execution engine
14//!
15//! The `lower` module converts analyzed AST into the runtime types.
16//!
17//! # Modules
18//!
19//! ## Pipeline
20//! - `raw`: Raw AST with `Spanned<T>` fields for precise error locations
21//! - `analyzed`: Validated AST with TaskId interning and semantic checks
22//! - `analyzer`: Validation and transformation (raw → analyzed)
23//! - `lower`: Lowering (analyzed → runtime types)
24//!
25//! ## Runtime Types
26//! - `workflow`: Workflow, Task, McpConfigInline
27//! - `action`: TaskAction, InferParams, ExecParams, FetchParams
28//! - `invoke`: InvokeParams (MCP integration)
29//! - `agent`: AgentParams (Agentic execution)
30//! - `output`: OutputPolicy, OutputFormat
31//! - `context`: ContextConfig (File loading at workflow start)
32//! - `agent_def`: AgentDef (Reusable agent configurations)
33//! - `skill_def`: SkillDef, SkillRef (Prompt augmentation)
34//! - `include`: IncludeSpec (DAG fusion)
35//! - `artifact`: ArtifactSpec, ArtifactsConfig (File persistence)
36//! - `logging`: LogConfig, LogLevel (Level-filtered logging)
37//!
38//! These types represent the "what" — static structure parsed from YAML.
39//! For execution, see the `runtime` module.
40
41// Pipeline stages — re-exported from nika-core (synced in v0.40.0)
42pub use nika_core::ast::analyzed;
43pub use nika_core::ast::analyzer;
44pub mod lower;
45pub use nika_core::ast::raw;
46pub mod schema {
47 pub use nika_core::ast::schema::*;
48}
49
50// Re-export from nika-core: identical type modules
51// These modules were deleted from nika (identical copies) and are now
52// served from nika-core to eliminate duplication.
53pub mod budget {
54 pub use nika_core::ast::budget::*;
55}
56pub mod content {
57 pub use nika_core::ast::content::*;
58}
59pub mod agent_def {
60 pub use nika_core::ast::agent_def::*;
61}
62pub mod artifact {
63 pub use nika_core::ast::artifact::*;
64}
65pub mod context {
66 pub use nika_core::ast::context::*;
67}
68pub mod decompose {
69 pub use nika_core::ast::decompose::*;
70}
71pub mod logging {
72 pub use nika_core::ast::logging::*;
73}
74pub mod output {
75 pub use nika_core::ast::output::*;
76}
77pub mod structured {
78 pub use nika_core::ast::structured::*;
79}
80
81#[cfg(test)]
82mod tests_200_workflows;
83
84// Re-export from nika-core: modules synced with CoreError variants
85pub mod completion {
86 pub use nika_core::ast::completion::*;
87}
88pub mod guardrails {
89 pub use nika_core::ast::guardrails::*;
90}
91pub mod include {
92 pub use nika_core::ast::include::*;
93}
94pub mod limits {
95 pub use nika_core::ast::limits::*;
96}
97
98// Runtime types (consumed by runner/executor — depend on NikaError)
99mod action;
100mod agent;
101pub mod import_loader;
102pub mod include_loader;
103mod invoke;
104pub mod loader;
105pub mod pkg_resolver;
106pub mod schema_validator;
107pub mod skill_def;
108mod workflow;
109
110// Re-export all public types
111pub use action::{ExecParams, FetchParams, InferParams, ResponseFormat, TaskAction};
112// AgentParams + ToolChoice are defined in agent.rs
113pub use agent::{AgentParams, ToolChoice};
114// AgentDef is defined in agent_def.rs
115pub use agent_def::AgentDef;
116// InvokeParams is defined in invoke.rs and re-exported here
117// (also used by action.rs for TaskAction::Invoke variant)
118pub use invoke::InvokeParams;
119// ContextConfig is defined in context.rs
120pub use context::ContextConfig;
121// IncludeSpec is defined in include.rs
122pub use include::IncludeSpec;
123pub use output::{OutputFormat, OutputPolicy, SchemaRef};
124// SkillDef + SkillRef are defined in skill_def.rs
125pub use skill_def::{SkillDef, SkillRef};
126// PkgUri is defined in pkg_resolver.rs
127pub use pkg_resolver::PkgUri;
128pub use workflow::{McpConfigInline, Task, Workflow};
129// DecomposeSpec is defined in decompose.rs
130pub use decompose::{DecomposeSpec, DecomposeStrategy};
131// Loader is defined in loader.rs
132pub use loader::{discover_definitions, load_definition, DefinitionKind, LoadedDefinition};
133// Import loader is defined in import_loader.rs
134pub use import_loader::expand_imports;
135// Include loader is defined in include_loader.rs
136pub use include_loader::expand_includes;
137// StructuredOutputSpec is defined in structured.rs
138pub use structured::StructuredOutputSpec;
139// CompletionConfig is defined in completion.rs
140pub use completion::{
141 CompletionConfig, CompletionMode, ConfidenceConfig, InstructionConfig, LowConfidenceAction,
142 PatternConfig, PatternType, SignalConfig, SignalFields,
143};
144// LimitsConfig is defined in limits.rs
145pub use limits::{LimitAction, LimitStatus, LimitType, LimitsConfig, OnLimitReachedConfig};
146pub use lower::{lower, unlower};
147
148// ============================================================================
149// Unified Pipeline: YAML → Raw → Analyzed → Workflow
150// ============================================================================
151
152use crate::ast::analyzed::AnalyzedWorkflow;
153use crate::error::NikaError;
154use crate::source::FileId;
155
156/// Parse a YAML workflow through the three-phase pipeline.
157///
158/// Pipeline: `YAML → raw::parse → analyzer::analyze → lower → Workflow`
159///
160/// Returns the runtime `Workflow` type consumed by `expand_includes` and `Runner`.
161///
162/// # Errors
163///
164/// - `NikaError::ParseError` — YAML syntax or structural errors (Phase 1)
165/// - `NikaError::ValidationError` — Semantic validation errors (Phase 2)
166pub fn parse_workflow(yaml: &str) -> Result<Workflow, NikaError> {
167 // Phase 1: YAML → Raw AST (with span tracking)
168 let raw = raw::parse(yaml, FileId(0)).map_err(|e| NikaError::ParseError {
169 details: format!("[{}] {}", e.kind.code(), e.message),
170 })?;
171
172 // Phase 2: Raw → Analyzed (validation, reference resolution)
173 let analyzed = analyzer::analyze(raw).into_result().map_err(|errors| {
174 let messages: Vec<String> = errors
175 .iter()
176 .map(|e| format!("[{}] {}", e.kind.code(), e))
177 .collect();
178 NikaError::ValidationError {
179 reason: messages.join("; "),
180 }
181 })?;
182
183 // Phase 3: Analyzed → Runtime Workflow
184 lower(analyzed)
185}
186
187/// Parse a YAML workflow and return the AnalyzedWorkflow directly.
188///
189/// Pipeline: `YAML → raw::parse → analyzer::analyze → AnalyzedWorkflow`
190///
191/// Skips the lowering step. The returned `AnalyzedWorkflow` is consumed
192/// directly by the `Runner`, which handles bridge conversions at the
193/// `TaskExecutor` boundary.
194///
195/// # Errors
196///
197/// - `NikaError::ParseError` — YAML syntax or structural errors (Phase 1)
198/// - `NikaError::ValidationError` — Semantic validation errors (Phase 2)
199pub fn parse_analyzed(yaml: &str) -> Result<AnalyzedWorkflow, NikaError> {
200 // Phase 1: YAML → Raw AST (with span tracking)
201 let raw = raw::parse(yaml, FileId(0)).map_err(|e| NikaError::ParseError {
202 details: format!("[{}] {}", e.kind.code(), e.message),
203 })?;
204
205 // Phase 2: Raw → Analyzed (validation, reference resolution)
206 analyzer::analyze(raw).into_result().map_err(|errors| {
207 let messages: Vec<String> = errors
208 .iter()
209 .map(|e| format!("[{}] {}", e.kind.code(), e))
210 .collect();
211 NikaError::ValidationError {
212 reason: messages.join("; "),
213 }
214 })
215}