orchestral_runtime/orchestrator/
entry.rs1use super::*;
2
3impl Orchestrator {
4 pub async fn handle_event(
6 &self,
7 event: Event,
8 ) -> Result<OrchestratorResult, OrchestratorError> {
9 if let Some(interaction_id) = self.thread_runtime.find_resume_interaction(&event).await {
10 return self.resume_interaction(interaction_id, event).await;
11 }
12
13 self.start_new_interaction(event).await
14 }
15
16 async fn start_new_interaction(
17 &self,
18 event: Event,
19 ) -> Result<OrchestratorResult, OrchestratorError> {
20 let event_clone = event.clone();
21 tracing::info!(
22 thread_id = %event_clone.thread_id(),
23 event_type = %event_type_label(&event_clone),
24 "orchestrator received event"
25 );
26 let decision = self.thread_runtime.handle_event(event).await?;
27
28 let (interaction_id, started_kind) = match &decision {
29 HandleEventResult::Started { interaction_id } => (interaction_id.clone(), "started"),
30 HandleEventResult::Merged { interaction_id } => {
31 self.emit_lifecycle_event(
35 "turn_merged",
36 Some(interaction_id.as_str()),
37 None,
38 Some("event merged into active interaction"),
39 serde_json::json!({
40 "event_type": event_type_label(&event_clone),
41 }),
42 )
43 .await;
44 return Ok(OrchestratorResult::Merged {
45 interaction_id: interaction_id.clone(),
46 task_id: "".into(), result: ExecutionResult::Completed,
48 });
49 }
50 HandleEventResult::Rejected { reason } => {
51 self.emit_lifecycle_event(
52 "turn_rejected",
53 None,
54 None,
55 Some(reason),
56 serde_json::json!({ "reason": reason }),
57 )
58 .await;
59 return Ok(OrchestratorResult::Rejected {
60 reason: reason.clone(),
61 });
62 }
63 HandleEventResult::Queued => {
64 self.emit_lifecycle_event(
65 "turn_queued",
66 None,
67 None,
68 Some("event queued"),
69 Value::Null,
70 )
71 .await;
72 return Ok(OrchestratorResult::Queued);
73 }
74 };
75
76 let intent = intent_from_event(&event_clone, Some(interaction_id.to_string()))?;
77 let task = Task::new(intent);
78 self.task_store.save(&task).await?;
79 self.thread_runtime
80 .add_task_to_interaction(interaction_id.as_str(), task.id.clone())
81 .await?;
82 self.emit_lifecycle_event(
83 "turn_started",
84 Some(interaction_id.as_str()),
85 Some(task.id.as_str()),
86 Some("turn started"),
87 serde_json::json!({
88 "started_kind": started_kind,
89 "event_type": event_type_label(&event_clone),
90 }),
91 )
92 .await;
93
94 self.run_planning_pipeline(interaction_id, started_kind, task)
95 .await
96 }
97
98 async fn resume_interaction(
99 &self,
100 interaction_id: InteractionId,
101 event: Event,
102 ) -> Result<OrchestratorResult, OrchestratorError> {
103 self.thread_runtime
104 .append_event_to_interaction(interaction_id.as_str(), event.clone())
105 .await?;
106 self.thread_runtime
107 .resume_interaction(interaction_id.as_str())
108 .await?;
109
110 let interaction = self
111 .thread_runtime
112 .get_interaction(interaction_id.as_str())
113 .await
114 .ok_or_else(|| OrchestratorError::ResumeError("interaction missing".to_string()))?;
115 let task_id =
116 interaction.task_ids.last().cloned().ok_or_else(|| {
117 OrchestratorError::ResumeError("interaction has no task".to_string())
118 })?;
119
120 let mut task = self
121 .task_store
122 .load(task_id.as_str())
123 .await?
124 .ok_or_else(|| OrchestratorError::TaskNotFound(task_id.to_string()))?;
125 self.emit_lifecycle_event(
126 "turn_resumed",
127 Some(interaction_id.as_str()),
128 Some(task.id.as_str()),
129 Some("resuming waiting turn"),
130 serde_json::json!({
131 "event_type": event_type_label(&event),
132 }),
133 )
134 .await;
135 if task.plan.is_none() {
136 let intent = intent_from_event(&event, Some(interaction_id.to_string()))?;
137 let new_task = Task::new(intent);
138 self.task_store.save(&new_task).await?;
139 self.thread_runtime
140 .add_task_to_interaction(interaction_id.as_str(), new_task.id.clone())
141 .await?;
142 self.emit_lifecycle_event(
143 "turn_started",
144 Some(interaction_id.as_str()),
145 Some(new_task.id.as_str()),
146 Some("turn started from clarification follow-up"),
147 serde_json::json!({
148 "started_kind": "merged",
149 "event_type": event_type_label(&event),
150 "resumed_from_task_id": task.id.clone(),
151 }),
152 )
153 .await;
154 return self
155 .run_planning_pipeline(interaction_id, "merged", new_task)
156 .await;
157 }
158 task.start_executing();
159 self.task_store.save(&task).await?;
160
161 self.emit_lifecycle_event(
162 "execution_started",
163 Some(interaction_id.as_str()),
164 Some(task.id.as_str()),
165 Some("execution resumed"),
166 serde_json::json!({ "resume": true }),
167 )
168 .await;
169 let result = self
170 .execute_existing_task(&mut task, interaction_id.as_str(), Some(&event))
171 .await?;
172 self.emit_lifecycle_event(
173 "execution_completed",
174 Some(interaction_id.as_str()),
175 Some(task.id.as_str()),
176 Some("execution completed"),
177 execution_result_metadata(&result),
178 )
179 .await;
180 self.emit_interpreted_output(interaction_id.as_str(), &task, &result)
181 .await;
182
183 Ok(OrchestratorResult::Merged {
184 interaction_id,
185 task_id: task.id.clone(),
186 result,
187 })
188 }
189}