stasis/application/orchestration/
sequential_pattern_pipeline.rs1use crate::application::orchestration::prompt_pipeline::{
2 PromptExecutionContext, PromptExecutionPipeline, PromptExecutionRequest,
3};
4use crate::application::runtime::chat_options_resolver::resolve_reasoning_effort;
5use crate::domain::errors::Result;
6
7#[derive(Clone, Debug)]
8pub struct SequentialPatternStage {
9 pub stage_id: String,
10 pub user_prompt_template: String,
11 pub system_prompt: Option<String>,
12 pub policy_profile: Option<String>,
13 pub model_hint: Option<String>,
14 pub reasoning_effort: Option<String>,
15}
16
17#[derive(Clone, Debug)]
18pub struct SequentialPatternExecutionRequest {
19 pub initial_user_prompt: String,
20 pub trace_id: Option<String>,
21 pub correlation_id: Option<String>,
22 pub policy_profile: Option<String>,
23 pub model_hint: Option<String>,
24 pub reasoning_effort: Option<String>,
25 pub stages: Vec<SequentialPatternStage>,
26}
27
28#[derive(Clone, Debug)]
29pub struct SequentialPatternStageResult {
30 pub stage_id: String,
31 pub rendered_prompt: String,
32 pub output_text: String,
33}
34
35#[derive(Clone, Debug)]
36pub struct SequentialPatternExecutionResponse {
37 pub final_text: String,
38 pub stages: Vec<SequentialPatternStageResult>,
39 pub termination_reason: String,
40}
41
42#[derive(Clone)]
43pub struct SequentialPatternPipeline {
44 prompt_pipeline: PromptExecutionPipeline,
45}
46
47impl SequentialPatternPipeline {
48 pub fn new(prompt_pipeline: PromptExecutionPipeline) -> Self {
49 Self { prompt_pipeline }
50 }
51
52 pub async fn execute(
53 &self,
54 request: SequentialPatternExecutionRequest,
55 ) -> Result<SequentialPatternExecutionResponse> {
56 let mut current_input = request.initial_user_prompt;
57 let mut stage_results = Vec::with_capacity(request.stages.len());
58
59 for stage in request.stages {
60 let rendered_prompt = stage
61 .user_prompt_template
62 .replace("{{input}}", ¤t_input)
63 .replace("{input}", ¤t_input);
64
65 let context = PromptExecutionContext {
66 trace_id: request.trace_id.clone(),
67 correlation_id: request.correlation_id.clone(),
68 policy_profile: stage
69 .policy_profile
70 .clone()
71 .or_else(|| request.policy_profile.clone()),
72 model_hint: stage
73 .model_hint
74 .clone()
75 .or_else(|| request.model_hint.clone()),
76 reasoning_effort: resolve_reasoning_effort(
77 stage.reasoning_effort.clone(),
78 request.reasoning_effort.clone(),
79 ),
80 };
81
82 let mut prompt_request =
83 PromptExecutionRequest::from_user_prompt(rendered_prompt.clone())
84 .with_context(context);
85 if let Some(system_prompt) = stage.system_prompt {
86 prompt_request = prompt_request.with_system_prompt(system_prompt);
87 }
88
89 let response = self.prompt_pipeline.execute(prompt_request).await?;
90 current_input = response.text.clone();
91 stage_results.push(SequentialPatternStageResult {
92 stage_id: stage.stage_id,
93 rendered_prompt,
94 output_text: response.text,
95 });
96 }
97
98 Ok(SequentialPatternExecutionResponse {
99 final_text: current_input,
100 stages: stage_results,
101 termination_reason: "completed_all_stages".to_string(),
102 })
103 }
104}