nika_engine/runtime/
context.rs1use std::sync::Arc;
10
11use crate::ast::analyzed::{AnalyzedWorkflow, TaskId, TaskTable};
12
13#[derive(Debug)]
18pub struct WorkflowMeta {
19 task_table: TaskTable,
21
22 provider: Option<String>,
24
25 model: Option<String>,
27}
28
29impl WorkflowMeta {
30 pub fn from_workflow(wf: &AnalyzedWorkflow) -> Arc<Self> {
32 Arc::new(Self {
33 task_table: wf.task_table.clone(),
34 provider: wf.provider.clone(),
35 model: wf.model.clone(),
36 })
37 }
38
39 pub fn task_name(&self, id: TaskId) -> Option<&str> {
44 self.task_table.get_name(id)
45 }
46
47 pub fn task_id(&self, name: &str) -> Option<TaskId> {
49 self.task_table.get_id(name)
50 }
51
52 pub fn provider(&self) -> Option<&str> {
54 self.provider.as_deref()
55 }
56
57 pub fn model(&self) -> Option<&str> {
59 self.model.as_deref()
60 }
61
62 pub fn task_table(&self) -> &TaskTable {
64 &self.task_table
65 }
66
67 pub fn task_count(&self) -> usize {
69 self.task_table.len()
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use crate::ast::analyzed::AnalyzedWorkflow;
77
78 fn make_workflow(
79 tasks: &[&str],
80 provider: Option<&str>,
81 model: Option<&str>,
82 ) -> AnalyzedWorkflow {
83 let mut wf = AnalyzedWorkflow::default();
84 for name in tasks {
85 wf.task_table.insert(name);
86 }
87 wf.provider = provider.map(String::from);
88 wf.model = model.map(String::from);
89 wf
90 }
91
92 #[test]
93 fn from_workflow_roundtrip() {
94 let wf = make_workflow(
95 &["step1", "step2", "step3"],
96 Some("anthropic"),
97 Some("claude-sonnet-4-6"),
98 );
99 let ctx = WorkflowMeta::from_workflow(&wf);
100
101 assert_eq!(ctx.task_count(), 3);
102 assert_eq!(ctx.provider(), Some("anthropic"));
103 assert_eq!(ctx.model(), Some("claude-sonnet-4-6"));
104 }
105
106 #[test]
107 fn task_name_roundtrip() {
108 let wf = make_workflow(&["alpha", "beta"], None, None);
109 let ctx = WorkflowMeta::from_workflow(&wf);
110
111 let id_alpha = ctx.task_id("alpha").unwrap();
112 let id_beta = ctx.task_id("beta").unwrap();
113
114 assert_eq!(ctx.task_name(id_alpha).unwrap(), "alpha");
115 assert_eq!(ctx.task_name(id_beta).unwrap(), "beta");
116 }
117
118 #[test]
119 fn unknown_name_returns_none() {
120 let wf = make_workflow(&["task1"], None, None);
121 let ctx = WorkflowMeta::from_workflow(&wf);
122
123 assert!(ctx.task_id("nonexistent").is_none());
124 }
125
126 #[test]
127 fn provider_and_model_none() {
128 let wf = make_workflow(&["task1"], None, None);
129 let ctx = WorkflowMeta::from_workflow(&wf);
130
131 assert!(ctx.provider().is_none());
132 assert!(ctx.model().is_none());
133 }
134
135 #[test]
136 fn task_table_accessible() {
137 let wf = make_workflow(&["a", "b", "c"], None, None);
138 let ctx = WorkflowMeta::from_workflow(&wf);
139
140 let table = ctx.task_table();
141 assert_eq!(table.len(), 3);
142 assert!(table.contains("a"));
143 assert!(table.contains("b"));
144 assert!(table.contains("c"));
145 }
146
147 #[test]
148 fn arc_sharing() {
149 let wf = make_workflow(&["task1"], Some("openai"), None);
150 let ctx = WorkflowMeta::from_workflow(&wf);
151
152 let ctx2 = Arc::clone(&ctx);
153 assert_eq!(ctx2.task_name(TaskId::new(0)).unwrap(), "task1");
154 assert_eq!(ctx2.provider(), Some("openai"));
155 }
156}